summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/core/socket.h6
-rw-r--r--src/socket.c135
2 files changed, 141 insertions, 0 deletions
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
index d2118ef0..6db436ae 100644
--- a/include/osmocom/core/socket.h
+++ b/include/osmocom/core/socket.h
@@ -8,6 +8,7 @@
* \file socket.h */
#include <stdint.h>
+#include <stdbool.h>
struct sockaddr;
struct osmo_fd;
@@ -47,4 +48,9 @@ int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
char *osmo_sock_get_name(void *ctx, int fd);
+int osmo_sock_mcast_loop_set(int fd, bool enable);
+int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl);
+int osmo_sock_mcast_all_set(int fd, bool enable);
+int osmo_sock_mcast_subscribe(int fd, const char *grp_addr);
+
/*! @} */
diff --git a/src/socket.c b/src/socket.c
index 28d9b1c4..d18f756f 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -40,6 +40,7 @@
#include <sys/un.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
@@ -614,6 +615,140 @@ local_only:
return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
}
+static int sock_get_domain(int fd)
+{
+ int domain;
+#ifdef SO_DOMAIN
+ socklen_t dom_len = sizeof(domain);
+ int rc;
+
+ rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
+ if (rc < 0)
+ return rc;
+#else
+ /* This of course sucks, but what shall we do on OSs like
+ * FreeBSD that don't seem to expose a method by which one can
+ * learn the address family of a socket? */
+ domain = AF_INET;
+#endif
+ return domain;
+}
+
+
+/*! Activate or de-activate local loop-back of transmitted multicast packets
+ * \param[in] fd file descriptor of related socket
+ * \param[in] enable Enable (true) or disable (false) loop-back
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_loop_set(int fd, bool enable)
+{
+ int domain, loop = 0;
+
+ if (enable)
+ loop = 1;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
+ case AF_INET6:
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Set the TTL of outbound multicast packets
+ * \param[in] fd file descriptor of related socket
+ * \param[in] ttl TTL of to-be-sent multicast packets
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
+{
+ int domain, ttli = ttl;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
+ case AF_INET6:
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
+ * \param[in] fd file descriptor of related socket
+ * \param[in] enable Enable or Disable receiving of all packets
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_all_set(int fd, bool enable)
+{
+ int domain, all = 0;
+
+ if (enable)
+ all = 1;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+#ifdef IP_MULTICAST_ALL
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
+#endif
+ case AF_INET6:
+ /* there seems no equivalent ?!? */
+ default:
+ return -EINVAL;
+ }
+}
+
+/* FreeBSD calls the socket option differently */
+#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+
+/*! Subscribe to the given IP multicast group
+ * \param[in] fd file descriptor of related scoket
+ * \param[in] grp_addr ASCII representation of the multicast group address
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
+{
+ int rc, domain;
+ struct ip_mreq mreq;
+ struct ipv6_mreq mreq6;
+ struct in6_addr i6a;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+#ifdef IPV6_ADD_MEMBERSHIP
+ case AF_INET6:
+ memset(&mreq6, 0, sizeof(mreq6));
+ rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
+ if (rc < 0)
+ return -EINVAL;
+ mreq6.ipv6mr_multiaddr = i6a;
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+
#endif /* HAVE_SYS_SOCKET_H */
/*! @} */