mirror of https://gerrit.osmocom.org/libosmocore
socket.c: Add multicast related convenience functions
Change-Id: Id703e7a7a1e065181a4c76c088b8dcc1b7fe15a2
This commit is contained in:
parent
ea91a51ebe
commit
e30d7e6018
|
@ -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);
|
||||
|
||||
/*! @} */
|
||||
|
|
135
src/socket.c
135
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 */
|
||||
|
||||
/*! @} */
|
||||
|
|
Loading…
Reference in New Issue