From 9f632ec6511cffb0c0954135c4f8680891127c46 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sat, 24 Jun 2023 10:40:07 +0200 Subject: [PATCH] New option ipv6-join-source-group --- CHANGES | 3 + config.h.in | 3 + configure.ac | 14 +++++ doc/socat.yo | 23 +++++++- xio-ip6.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++ xio-ip6.h | 4 ++ xioopts.c | 29 ++++++++++ xioopts.h | 4 ++ 8 files changed, 229 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index de98545..0d25d9c 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,9 @@ Features: not internally used by Socat. Tests: SIGTERM_NOLOG SIG31_LOG + Added option ipv6-join-source-group. + Thanks to Martin Buck and David Schweizer for sending patches. + Corrections: When a sub process (EXEC, SYSTEM) terminated with exit code other than 0, its last sent data might have been lost depending on timing of read/ diff --git a/config.h.in b/config.h.in index 5cbdbbd..7ee8677 100644 --- a/config.h.in +++ b/config.h.in @@ -388,6 +388,9 @@ /* Define if you have struct ip_mreq_source */ #undef HAVE_STRUCT_IP_MREQ_SOURCE +/* Define if you have struct group_source_req */ +#undef HAVE_STRUCT_GROUP_SOURCE_REQ + /* Define if you have struct ifreq */ #undef HAVE_STRUCT_IFREQ diff --git a/configure.ac b/configure.ac index f1457be..61bfca9 100644 --- a/configure.ac +++ b/configure.ac @@ -1256,6 +1256,20 @@ if test $sc_cv_struct_ip_mreq_source = yes; then fi AC_MSG_RESULT($sc_cv_struct_ip_mreqn) +# struct group_source_req (for multicasting options) +AC_MSG_CHECKING(for struct group_source_req) +AC_CACHE_VAL(sc_cv_struct_group_source_req, +[AC_TRY_COMPILE([#include +#include +#include +#include ],[struct group_source_req s;], +[sc_cv_struct_group_source_req=yes], +[sc_cv_struct_group_source_req=no])]) +if test $sc_cv_struct_group_source_req = yes; then + AC_DEFINE(HAVE_STRUCT_GROUP_SOURCE_REQ) +fi +AC_MSG_RESULT($sc_cv_struct_group_source_req) + # struct ifreq (for network interfaces) AC_MSG_CHECKING(for struct ifreq) diff --git a/doc/socat.yo b/doc/socat.yo index 3cee60f..f7eb401 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -413,6 +413,7 @@ label(ADDRESS_IP_DATAGRAM)dit(bf(tt(IP-DATAGRAM:
:))) link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP), + link(ipv6-join-source-group)(OPTION_IPV6_JOIN_SOURCE_GROUP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), link(pf)(OPTION_PROTOCOL_FAMILY)nl() @@ -1129,6 +1130,7 @@ label(ADDRESS_UDP_DATAGRAM)dit(bf(tt(UDP-DATAGRAM:
:))) link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP), + link(ipv6-join-source-group)(OPTION_IPV6_JOIN_SOURCE_GROUP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), link(sourceport)(OPTION_SOURCEPORT), @@ -2172,9 +2174,13 @@ dit(bf(tt(ip-add-membership=))) - Makes the socket member of the specified multicast group for the specified - source, i.e. only multicast traffic from this address is to be delivered. - This is currently only implemented for IPv4.nl() + Makes the socket member of the specified multicast group for the + specified source, i.e. only multicast traffic from this address is to be + delivered. This only works for IPv4, see + link(ipv6-join-source-group)(OPTION_IPV6_JOIN_SOURCE_GROUP) for the IPv6 + variant. The option takes the IP address of the multicast group, the IP + address of the desired network interface and the source IP address of the + multicast traffic. label(OPTION_IPV6_JOIN_GROUP) dit(bf(tt(ipv6-join-group=))) dit(bf(tt(ipv6-join-group=))) @@ -2184,6 +2190,17 @@ dit(bf(tt(ipv6-join-group=))) group and info about the desired network interface. The indices of active network interfaces can be shown using the utility procan(). +label(OPTION_IPV6_JOIN_SOURCE_GROUP) +dit(bf(tt(ipv6-join-source-group=))) +dit(bf(tt(ipv6-join-source-group=))) + Makes the socket member of the specified multicast group for the + specified source, i.e. only multicast traffic from this address is to be + delivered. This only works for IPv6, see + link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP) for the + IPv4 variant. The option takes the IP address of the multicast group, + info about the desired network interface and the source IP address of the + multicast traffic. The indices of active network interfaces can be shown + using the utility procan(). label(OPTION_IP_MULTICAST_IF) dit(bf(tt(ip-multicast-if=))) Specifies hostname or address of the network interface to be used for diff --git a/xio-ip6.c b/xio-ip6.c index 682f62a..fbcadcf 100644 --- a/xio-ip6.c +++ b/xio-ip6.c @@ -14,6 +14,7 @@ #include "xio-ip.h" /* xiogetaddrinfo() */ #include "xio-ip6.h" +#include "nestlex.h" static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen); @@ -25,6 +26,9 @@ const struct optdesc opt_ipv6_v6only = { "ipv6-v6only", "ipv6only", OPT_IPV6_V6O #ifdef IPV6_JOIN_GROUP const struct optdesc opt_ipv6_join_group = { "ipv6-join-group", "join-group", OPT_IPV6_JOIN_GROUP, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_IP_MREQN, OFUNC_SOCKOPT, SOL_IPV6, IPV6_JOIN_GROUP }; #endif +#ifdef MCAST_JOIN_SOURCE_GROUP +const struct optdesc opt_ipv6_join_source_group = { "ipv6-join-source-group", "join-source-group", OPT_IPV6_JOIN_SOURCE_GROUP, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_GROUP_SOURCE_REQ, OFUNC_SOCKOPT, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP }; +#endif #ifdef IPV6_PKTINFO const struct optdesc opt_ipv6_pktinfo = { "ipv6-pktinfo", "pktinfo", OPT_IPV6_PKTINFO, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_PKTINFO }; #endif @@ -502,4 +506,152 @@ int xioapply_ipv6_join_group( } #endif /* defined(HAVE_STRUCT_IPV6_MREQ) */ +#if HAVE_STRUCT_GROUP_SOURCE_REQ +int xiotype_ip6_join_source_group( + char *token, const struct optname *ent, struct opt *opt) +{ + /* We do not resolve the addresses here because we do not yet know + if we are coping with an IPv4 or IPv6 socat address */ + const char *ends[] = { ":", NULL }; + const char *nests[] = { "[","]", NULL }; + char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1; + char *tokp = token; + int parsres; + + /* Parse first IP address (mcast group), expect ':' */ + parsres = + nestlex((const char **)&tokp, &buffp, &bufspc, + ends, NULL, NULL, nests, + true, false, false); + if (parsres < 0) { + Error1("option too long: \"%s\"", token); + return -1; + } else if (parsres > 0) { + Error1("syntax error in \"%s\"", token); + return -1; + } + if (*tokp != ':') { + Error1("syntax in option %s: missing ':'", token); + } + *buffp++ = '\0'; + if ((opt->value.u_string/*mcaddr*/ = strdup(buff)) == NULL) { + int _errno = errno; + Error1("strdup(\"%s\"): out of memory", buff); + errno = _errno; + return -1; + } + + ++tokp; + /* Parse interface name/index, expect ':' or '\0'' */ + buffp = buff; + parsres = + nestlex((const char **)&tokp, &buffp, &bufspc, + ends, NULL, NULL, nests, + true, false, false); + if (parsres < 0) { + Error1("option too long: \"%s\"", token); + return -1; + } else if (parsres > 0) { + Error1("syntax error in \"%s\"", token); + return -1; + } + if (*tokp != ':') { + Error1("syntax in option %s: missing ':'", token); + } + *buffp++ = '\0'; + if ((opt->value2.u_string/*ifindex*/ = Malloc(IF_NAMESIZE)) == NULL) { + int _errno = errno; + free(opt->value.u_string); + errno = _errno; + return -1; + } + strncpy(opt->value2.u_string/*ifindex*/, buff, IF_NAMESIZE); + + ++tokp; + /* Parse second IP address (source address), expect ':' or '\0'' */ + buffp = buff; + parsres = + nestlex((const char **)&tokp, &buffp, &bufspc, + ends, NULL, NULL, nests, + true, false, false); + if (parsres < 0) { + Error1("option too long: \"%s\"", token); + return -1; + } else if (parsres > 0) { + Error1("syntax error in \"%s\"", token); + return -1; + } + if (*tokp) { + Error1("syntax in option %s: trailing cruft", token); + } + *buffp++ = '\0'; + if ((opt->value3.u_string/*srcaddr*/ = strdup(buff)) == NULL) { + int _errno = errno; + Error1("strdup(\"%s\"): out of memory", buff); + free(opt->value.u_string); + errno = _errno; + return -1; + } + + Info4("setting option \"%s\" to {\"%s\",\"%s\",\"%s\"}", + ent->desc->defname, + opt->value.u_string/*mcaddr*/, + opt->value2.u_string/*ifindex*/, + opt->value3.u_string/*srcaddr*/); + + if (!xioparms.experimental) { + Warn("option ipv6-join-source-group is experimental"); + } + + return 0; +} + +int xioapply_ip6_join_source_group(struct single *xfd, struct opt *opt) { + struct group_source_req ip6_gsr = {0}; + union sockaddr_union sockaddr1; + socklen_t socklen1 = sizeof(sockaddr1.ip6); + union sockaddr_union sockaddr2; + socklen_t socklen2 = sizeof(sockaddr2.ip6); + int res; + + /* First parameter is always multicast address */ + if ((res = + xiogetaddrinfo(opt->value.u_string/*mcaddr*/, NULL, + xfd->para.socket.la.soa.sa_family, + SOCK_DGRAM, IPPROTO_IP, + &sockaddr1, &socklen1, 0, 0)) != STAT_OK) { + return res; + } + memcpy(&ip6_gsr.gsr_group, &sockaddr1.ip6, socklen1); + /* Second parameter is interface name/index */ + if (ifindex(opt->value2.u_string/*ifindex*/, + &ip6_gsr.gsr_interface, -1) + < 0) { + Error1("interface \"%s\" not found", + opt->value.u_string/*ifindex*/); + ip6_gsr.gsr_interface = 0; + } + /* Third parameter is source address */ + if ((res = + xiogetaddrinfo(opt->value3.u_string/*srcaddr*/, NULL, + xfd->para.socket.la.soa.sa_family, + SOCK_DGRAM, IPPROTO_IP, + &sockaddr2, &socklen2, 0, 0)) != STAT_OK) { + return res; + } + memcpy(&ip6_gsr.gsr_source, &sockaddr2.ip6, socklen2); + if (Setsockopt(xfd->fd, opt->desc->major, opt->desc->minor, + &ip6_gsr, sizeof(ip6_gsr)) < 0) { + Error6("setsockopt(%d, %d, %d, {%d,...}, "F_Zu"): %s", + xfd->fd, opt->desc->major, opt->desc->minor, + ip6_gsr.gsr_interface, + sizeof(ip6_gsr), + strerror(errno)); + opt->desc = ODESC_ERROR; + return -1; + } + return 0; +} +#endif /* HAVE_STRUCT_GROUP_SOURCE_REQ */ + #endif /* WITH_IP6 */ diff --git a/xio-ip6.h b/xio-ip6.h index 6da27e1..d5dc7c2 100644 --- a/xio-ip6.h +++ b/xio-ip6.h @@ -13,6 +13,7 @@ extern const struct optdesc opt_ipv6_v6only; extern const struct optdesc opt_ipv6_join_group; +extern const struct optdesc opt_ipv6_join_source_group; extern const struct optdesc opt_ipv6_pktinfo; extern const struct optdesc opt_ipv6_recvpktinfo; extern const struct optdesc opt_ipv6_rthdr; @@ -50,6 +51,9 @@ xiosetsockaddrenv_ip6(int idx, char *namebuff, size_t namelen, struct sockaddr_in6 *sa, int ipproto); extern int xioapply_ipv6_join_group(xiosingle_t *xfd, struct opt *opt); +extern int xiotype_ip6_join_source_group(char* token, const struct optname *ent, struct opt *opt); +extern int xioapply_ip6_join_source_group(struct single *xfd, struct opt *opt); + #endif /* WITH_IP6 */ #endif /* !defined(__xio_ip6_h_included) */ diff --git a/xioopts.c b/xioopts.c index 64a4793..d93dcaf 100644 --- a/xioopts.c +++ b/xioopts.c @@ -786,6 +786,9 @@ const struct optname optionnames[] = { #ifdef IPV6_JOIN_GROUP IF_IP6 ("ipv6-add-membership", &opt_ipv6_join_group) #endif +#ifdef MCAST_JOIN_SOURCE_GROUP + IF_IP6 ("ipv6-add-source-membership", &opt_ipv6_join_source_group) +#endif #ifdef IPV6_AUTHHDR IF_IP6 ("ipv6-authhdr", &opt_ipv6_authhdr) #endif @@ -804,6 +807,9 @@ const struct optname optionnames[] = { #ifdef IPV6_JOIN_GROUP IF_IP6 ("ipv6-join-group", &opt_ipv6_join_group) #endif +#ifdef MCAST_JOIN_SOURCE_GROUP + IF_IP6 ("ipv6-join-source-group", &opt_ipv6_join_source_group) +#endif #ifdef IPV6_PKTINFO IF_IP6 ("ipv6-pktinfo", &opt_ipv6_pktinfo) #endif @@ -856,6 +862,9 @@ const struct optname optionnames[] = { #ifdef IPV6_JOIN_GROUP IF_IP6 ("join-group", &opt_ipv6_join_group) #endif +#ifdef MCAST_JOIN_SOURCE_GROUP + IF_IP6 ("join-source-group", &opt_ipv6_join_source_group) +#endif #if WITH_FS && defined(FS_JOURNAL_DATA_FL) IF_ANY ("journal", &opt_fs_journal_data) IF_ANY ("journal-data", &opt_fs_journal_data) @@ -2490,6 +2499,12 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts, break; #endif +#if HAVE_STRUCT_GROUP_SOURCE_REQ + case TYPE_GROUP_SOURCE_REQ: + xiotype_ip6_join_source_group(token, ent, opt); + break; +#endif + #if WITH_IP4 case TYPE_IP4NAME: { @@ -3331,6 +3346,12 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) { ++opt; continue; #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */ +#if defined(HAVE_STRUCT_GROUP_SOURCE_REQ) + case TYPE_GROUP_SOURCE_REQ: + /* handled in applyopts_single */ + ++opt; continue; +#endif /* defined(HAVE_STRUCT_GROUP_SOURCE_REQ) */ + /*! still many types missing; implement on demand */ #if WITH_IP4 case TYPE_IP4NAME: @@ -3984,6 +4005,14 @@ int applyopts_single(struct single *xfd, struct opt *opts, enum e_phase phase) { } break; #endif /* WITH_IP6 && defined(HAVE_STRUCT_IPV6_MREQ) */ + +#if WITH_IP6 && defined(HAVE_STRUCT_GROUP_SOURCE_REQ) + case OPT_IPV6_JOIN_SOURCE_GROUP: + if (xioapply_ip6_join_source_group(xfd, opt) < 0) { + continue; + } + break; +#endif /* WITH_IP6 && defined(HAVE_STRUCT_IPV6_MREQ) */ default: /* ignore here */ ++opt; continue; diff --git a/xioopts.h b/xioopts.h index 2e2f808..084cb7b 100644 --- a/xioopts.h +++ b/xioopts.h @@ -76,6 +76,9 @@ enum e_types { #if HAVE_STRUCT_IP_MREQ_SOURCE TYPE_IP_MREQ_SOURCE, /* for struct ip_mreq_source */ #endif +#if HAVE_STRUCT_GROUP_SOURCE_REQ + TYPE_GROUP_SOURCE_REQ, /* for struct group_source_req */ +#endif TYPE_GENERIC, /* type is determined from (text) data provided (dalan syntax) */ } ; @@ -370,6 +373,7 @@ enum e_optcode { OPT_IPV6_HOPLIMIT, OPT_IPV6_HOPOPTS, OPT_IPV6_JOIN_GROUP, + OPT_IPV6_JOIN_SOURCE_GROUP, OPT_IPV6_PKTINFO, OPT_IPV6_RECVDSTOPTS, OPT_IPV6_RECVERR,