New option ipv6-join-source-group

This commit is contained in:
Gerhard Rieger 2023-06-24 10:40:07 +02:00
parent 003ca09721
commit 9f632ec651
8 changed files with 229 additions and 3 deletions

View file

@ -17,6 +17,9 @@ Features:
not internally used by Socat. not internally used by Socat.
Tests: SIGTERM_NOLOG SIG31_LOG Tests: SIGTERM_NOLOG SIG31_LOG
Added option ipv6-join-source-group.
Thanks to Martin Buck and David Schweizer for sending patches.
Corrections: Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than 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/ 0, its last sent data might have been lost depending on timing of read/

View file

@ -388,6 +388,9 @@
/* Define if you have struct ip_mreq_source */ /* Define if you have struct ip_mreq_source */
#undef 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 */ /* Define if you have struct ifreq */
#undef HAVE_STRUCT_IFREQ #undef HAVE_STRUCT_IFREQ

View file

@ -1256,6 +1256,20 @@ if test $sc_cv_struct_ip_mreq_source = yes; then
fi fi
AC_MSG_RESULT($sc_cv_struct_ip_mreqn) 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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>],[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) # struct ifreq (for network interfaces)
AC_MSG_CHECKING(for struct ifreq) AC_MSG_CHECKING(for struct ifreq)

View file

@ -413,6 +413,7 @@ label(ADDRESS_IP_DATAGRAM)dit(bf(tt(IP-DATAGRAM:<address>:<protocol>)))
link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP),
link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP),
link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP), link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP),
link(ipv6-join-source-group)(OPTION_IPV6_JOIN_SOURCE_GROUP),
link(ttl)(OPTION_TTL), link(ttl)(OPTION_TTL),
link(tos)(OPTION_TOS), link(tos)(OPTION_TOS),
link(pf)(OPTION_PROTOCOL_FAMILY)nl() link(pf)(OPTION_PROTOCOL_FAMILY)nl()
@ -1129,6 +1130,7 @@ label(ADDRESS_UDP_DATAGRAM)dit(bf(tt(UDP-DATAGRAM:<address>:<port>)))
link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP),
link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP),
link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP), link(ipv6-join-group)(OPTION_IPV6_JOIN_GROUP),
link(ipv6-join-source-group)(OPTION_IPV6_JOIN_SOURCE_GROUP),
link(ttl)(OPTION_TTL), link(ttl)(OPTION_TTL),
link(tos)(OPTION_TOS), link(tos)(OPTION_TOS),
link(sourceport)(OPTION_SOURCEPORT), link(sourceport)(OPTION_SOURCEPORT),
@ -2172,9 +2174,13 @@ dit(bf(tt(ip-add-membership=<multicast-address:interface-address:interface-index
procan(). procan().
label(OPTION_IP_ADD_SOURCE_MEMBERSHIP) label(OPTION_IP_ADD_SOURCE_MEMBERSHIP)
dit(bf(tt(ip-add-source-membership=<multicast-address:interface-address:source-address>))) dit(bf(tt(ip-add-source-membership=<multicast-address:interface-address:source-address>)))
Makes the socket member of the specified multicast group for the specified Makes the socket member of the specified multicast group for the
source, i.e. only multicast traffic from this address is to be delivered. specified source, i.e. only multicast traffic from this address is to be
This is currently only implemented for IPv4.nl() 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) label(OPTION_IPV6_JOIN_GROUP)
dit(bf(tt(ipv6-join-group=<multicast-address:interface-name>))) dit(bf(tt(ipv6-join-group=<multicast-address:interface-name>)))
dit(bf(tt(ipv6-join-group=<multicast-address:interface-index>))) dit(bf(tt(ipv6-join-group=<multicast-address:interface-index>)))
@ -2184,6 +2190,17 @@ dit(bf(tt(ipv6-join-group=<multicast-address:interface-index>)))
group and info about the desired network interface. group and info about the desired network interface.
The indices of active network interfaces can be shown using the utility The indices of active network interfaces can be shown using the utility
procan(). procan().
label(OPTION_IPV6_JOIN_SOURCE_GROUP)
dit(bf(tt(ipv6-join-source-group=<multicast-address:interface-name:source-address>)))
dit(bf(tt(ipv6-join-source-group=<multicast-address:interface-index:source-address>)))
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) label(OPTION_IP_MULTICAST_IF)
dit(bf(tt(ip-multicast-if=<hostname>))) dit(bf(tt(ip-multicast-if=<hostname>)))
Specifies hostname or address of the network interface to be used for Specifies hostname or address of the network interface to be used for

152
xio-ip6.c
View file

@ -14,6 +14,7 @@
#include "xio-ip.h" /* xiogetaddrinfo() */ #include "xio-ip.h" /* xiogetaddrinfo() */
#include "xio-ip6.h" #include "xio-ip6.h"
#include "nestlex.h"
static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen); 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 #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 }; 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 #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 #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 }; 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 #endif
@ -502,4 +506,152 @@ int xioapply_ipv6_join_group(
} }
#endif /* defined(HAVE_STRUCT_IPV6_MREQ) */ #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 */ #endif /* WITH_IP6 */

View file

@ -13,6 +13,7 @@
extern const struct optdesc opt_ipv6_v6only; extern const struct optdesc opt_ipv6_v6only;
extern const struct optdesc opt_ipv6_join_group; 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_pktinfo;
extern const struct optdesc opt_ipv6_recvpktinfo; extern const struct optdesc opt_ipv6_recvpktinfo;
extern const struct optdesc opt_ipv6_rthdr; 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); struct sockaddr_in6 *sa, int ipproto);
extern int xioapply_ipv6_join_group(xiosingle_t *xfd, struct opt *opt); 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 /* WITH_IP6 */
#endif /* !defined(__xio_ip6_h_included) */ #endif /* !defined(__xio_ip6_h_included) */

View file

@ -786,6 +786,9 @@ const struct optname optionnames[] = {
#ifdef IPV6_JOIN_GROUP #ifdef IPV6_JOIN_GROUP
IF_IP6 ("ipv6-add-membership", &opt_ipv6_join_group) IF_IP6 ("ipv6-add-membership", &opt_ipv6_join_group)
#endif #endif
#ifdef MCAST_JOIN_SOURCE_GROUP
IF_IP6 ("ipv6-add-source-membership", &opt_ipv6_join_source_group)
#endif
#ifdef IPV6_AUTHHDR #ifdef IPV6_AUTHHDR
IF_IP6 ("ipv6-authhdr", &opt_ipv6_authhdr) IF_IP6 ("ipv6-authhdr", &opt_ipv6_authhdr)
#endif #endif
@ -804,6 +807,9 @@ const struct optname optionnames[] = {
#ifdef IPV6_JOIN_GROUP #ifdef IPV6_JOIN_GROUP
IF_IP6 ("ipv6-join-group", &opt_ipv6_join_group) IF_IP6 ("ipv6-join-group", &opt_ipv6_join_group)
#endif #endif
#ifdef MCAST_JOIN_SOURCE_GROUP
IF_IP6 ("ipv6-join-source-group", &opt_ipv6_join_source_group)
#endif
#ifdef IPV6_PKTINFO #ifdef IPV6_PKTINFO
IF_IP6 ("ipv6-pktinfo", &opt_ipv6_pktinfo) IF_IP6 ("ipv6-pktinfo", &opt_ipv6_pktinfo)
#endif #endif
@ -856,6 +862,9 @@ const struct optname optionnames[] = {
#ifdef IPV6_JOIN_GROUP #ifdef IPV6_JOIN_GROUP
IF_IP6 ("join-group", &opt_ipv6_join_group) IF_IP6 ("join-group", &opt_ipv6_join_group)
#endif #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 WITH_FS && defined(FS_JOURNAL_DATA_FL)
IF_ANY ("journal", &opt_fs_journal_data) IF_ANY ("journal", &opt_fs_journal_data)
IF_ANY ("journal-data", &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; break;
#endif #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 #if WITH_IP4
case TYPE_IP4NAME: case TYPE_IP4NAME:
{ {
@ -3331,6 +3346,12 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
++opt; continue; ++opt; continue;
#endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */ #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 */ /*! still many types missing; implement on demand */
#if WITH_IP4 #if WITH_IP4
case TYPE_IP4NAME: case TYPE_IP4NAME:
@ -3984,6 +4005,14 @@ int applyopts_single(struct single *xfd, struct opt *opts, enum e_phase phase) {
} }
break; break;
#endif /* WITH_IP6 && defined(HAVE_STRUCT_IPV6_MREQ) */ #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: default:
/* ignore here */ /* ignore here */
++opt; continue; ++opt; continue;

View file

@ -76,6 +76,9 @@ enum e_types {
#if HAVE_STRUCT_IP_MREQ_SOURCE #if HAVE_STRUCT_IP_MREQ_SOURCE
TYPE_IP_MREQ_SOURCE, /* for struct ip_mreq_source */ TYPE_IP_MREQ_SOURCE, /* for struct ip_mreq_source */
#endif #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) */ TYPE_GENERIC, /* type is determined from (text) data provided (dalan syntax) */
} ; } ;
@ -370,6 +373,7 @@ enum e_optcode {
OPT_IPV6_HOPLIMIT, OPT_IPV6_HOPLIMIT,
OPT_IPV6_HOPOPTS, OPT_IPV6_HOPOPTS,
OPT_IPV6_JOIN_GROUP, OPT_IPV6_JOIN_GROUP,
OPT_IPV6_JOIN_SOURCE_GROUP,
OPT_IPV6_PKTINFO, OPT_IPV6_PKTINFO,
OPT_IPV6_RECVDSTOPTS, OPT_IPV6_RECVDSTOPTS,
OPT_IPV6_RECVERR, OPT_IPV6_RECVERR,