summaryrefslogtreecommitdiff
path: root/src/platform/posix/posix_udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/posix/posix_udp.c')
-rw-r--r--src/platform/posix/posix_udp.c128
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