diff options
Diffstat (limited to 'src/platform/posix')
| -rw-r--r-- | src/platform/posix/posix_udp.c | 128 |
1 files changed, 121 insertions, 7 deletions
diff --git a/src/platform/posix/posix_udp.c b/src/platform/posix/posix_udp.c index 735953fa..4ef4c68c 100644 --- a/src/platform/posix/posix_udp.c +++ b/src/platform/posix/posix_udp.c @@ -1,5 +1,5 @@ // -// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a @@ -9,6 +9,9 @@ // #include "core/nng_impl.h" +#include "nng/nng.h" +#include "platform/posix/posix_impl.h" +#include <sys/errno.h> #ifdef NNG_PLATFORM_POSIX #include "platform/posix/posix_pollq.h" @@ -27,6 +30,22 @@ #define MSG_NOSIGNAL 0 #endif +#ifndef NNG_HAVE_INET6 +#undef NNG_ENABLE_IPV6 +#endif + +// Linux has IPV6_ADD_MEMBERSHIP and IPV6_DROP_MEMBERSHIP +#ifndef IPV6_JOIN_GROUP +#ifdef IPV6_ADD_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif +#endif +#ifndef IPV6_LEAVE_GROUP +#ifdef IPV6_DROP_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif +#endif + struct nni_plat_udp { nni_posix_pfd *udp_pfd; int udp_fd; @@ -56,15 +75,15 @@ nni_posix_udp_doclose(nni_plat_udp *udp) static void nni_posix_udp_dorecv(nni_plat_udp *udp) { - nni_aio * aio; + nni_aio *aio; nni_list *q = &udp->udp_recvq; // While we're able to recv, do so. while ((aio = nni_list_first(q)) != NULL) { struct iovec iov[4]; unsigned niov; - nni_iov * aiov; + nni_iov *aiov; struct sockaddr_storage ss; - nng_sockaddr * sa; + nng_sockaddr *sa; struct msghdr hdr = { .msg_name = NULL }; int rv = 0; int cnt = 0; @@ -102,7 +121,7 @@ nni_posix_udp_dorecv(nni_plat_udp *udp) static void nni_posix_udp_dosend(nni_plat_udp *udp) { - nni_aio * aio; + nni_aio *aio; nni_list *q = &udp->udp_sendq; // While we're able to send, do so. @@ -118,7 +137,7 @@ nni_posix_udp_dosend(nni_plat_udp *udp) rv = NNG_EADDRINVAL; } else { unsigned niov; - nni_iov * aiov; + nni_iov *aiov; struct iovec iov[16]; nni_aio_get_iov(aio, &niov, &aiov); @@ -192,7 +211,7 @@ nni_posix_udp_cb(nni_posix_pfd *pfd, unsigned events, void *arg) int nni_plat_udp_open(nni_plat_udp **upp, nni_sockaddr *bindaddr) { - nni_plat_udp * udp; + nni_plat_udp *udp; int salen; struct sockaddr_storage sa; int rv; @@ -323,4 +342,99 @@ nni_plat_udp_sockname(nni_plat_udp *udp, nni_sockaddr *sa) return (nni_posix_sockaddr2nn(sa, &ss, sz)); } +// Joining a multicast group is different than binding to a multicast +// group. This allows to receive both unicast and multicast at the given +// address. +static int +ip4_multicast_member(nni_plat_udp *udp, struct sockaddr *sa, bool join) +{ + struct ip_mreq mreq; + struct sockaddr_in *sin; + struct sockaddr_storage local; + socklen_t sz = sizeof(local); + + if (getsockname(udp->udp_fd, (struct sockaddr *) &local, &sz) >= 0) { + if (local.ss_family != AF_INET) { + // address families have to match + return (NNG_EADDRINVAL); + } + sin = (struct sockaddr_in *) &local; + mreq.imr_interface.s_addr = sin->sin_addr.s_addr; + } else { + mreq.imr_interface.s_addr = INADDR_ANY; + } + + // Determine our local interface + sin = (struct sockaddr_in *) sa; + + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + if (setsockopt(udp->udp_fd, IPPROTO_IP, + join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) == 0) { + return (0); + } + return (nni_plat_errno(errno)); +} + +#ifdef NNG_ENABLE_IPV6 +static int +ip6_multicast_member(nni_plat_udp *udp, struct sockaddr *sa, bool join) +{ + struct ipv6_mreq mreq; + struct sockaddr_in6 *sin6; + struct sockaddr_storage local; + socklen_t sz = sizeof(local); + + if (getsockname(udp->udp_fd, (struct sockaddr *) &local, &sz) >= 0) { + if (local.ss_family != AF_INET6) { + // address families have to match + return (NNG_EADDRINVAL); + } + sin6 = (struct sockaddr_in6 *) &local; + mreq.ipv6mr_interface = sin6->sin6_scope_id; + } else { + mreq.ipv6mr_interface = 0; + } + + // Determine our local interface + sin6 = (struct sockaddr_in6 *) sa; + + mreq.ipv6mr_multiaddr = sin6->sin6_addr; + if (setsockopt(udp->udp_fd, IPPROTO_IPV6, + join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) == 0) { + return (0); + } + return (nni_plat_errno(errno)); +} +#endif + +int +nni_plat_udp_multicast_membership( + nni_plat_udp *udp, nni_sockaddr *sa, bool join) +{ + struct sockaddr_storage ss; + socklen_t sz; + int rv; + + sz = nni_posix_nn2sockaddr(&ss, sa); + if (sz < 1) { + return (NNG_EADDRINVAL); + } + switch (ss.ss_family) { + case AF_INET: + rv = ip4_multicast_member(udp, (struct sockaddr *) &ss, join); + break; +#ifdef NNG_ENABLE_IPV6 + case AF_INET6: + rv = ip6_multicast_member(udp, (struct sockaddr *) &ss, join); + break; +#endif + default: + rv = NNG_EADDRINVAL; + } + + return (rv); +} + #endif // NNG_PLATFORM_POSIX |
