diff --git a/include/re_udp.h b/include/re_udp.h index cc3d895..426fc19 100644 --- a/include/re_udp.h +++ b/include/re_udp.h @@ -35,6 +35,9 @@ int udp_thread_attach(struct udp_sock *us); void udp_thread_detach(struct udp_sock *us); int udp_sock_fd(const struct udp_sock *us, int af); +int udp_multicast_join(struct udp_sock *us, const struct sa *group); +int udp_multicast_leave(struct udp_sock *us, const struct sa *group); + /* Helper API */ typedef bool (udp_helper_send_h)(int *err, struct sa *dst, diff --git a/src/udp/mcast.c b/src/udp/mcast.c new file mode 100644 index 0000000..5281f29 --- /dev/null +++ b/src/udp/mcast.c @@ -0,0 +1,69 @@ +/** + * @file mcast.c UDP Multicast + * + * Copyright (C) 2010 Creytiv.com + */ + +#define _BSD_SOURCE 1 +#include +#include +#include +#include + + +static int multicast_update(struct udp_sock *us, const struct sa *group, + bool join) +{ + struct ip_mreq mreq; +#ifdef HAVE_INET6 + struct ipv6_mreq mreq6; +#endif + int err; + + if (!us || !group) + return EINVAL; + + switch (sa_af(group)) { + + case AF_INET: + mreq.imr_multiaddr = group->u.in.sin_addr; + mreq.imr_interface.s_addr = 0; + + err = udp_setsockopt(us, IPPROTO_IP, + join + ? IP_ADD_MEMBERSHIP + : IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)); + break; + +#ifdef HAVE_INET6 + case AF_INET6: + mreq6.ipv6mr_multiaddr = group->u.in6.sin6_addr; + mreq6.ipv6mr_interface = 0; + + err = udp_setsockopt(us, IPPROTO_IPV6, + join + ? IPV6_JOIN_GROUP + : IPV6_LEAVE_GROUP, + &mreq6, sizeof(mreq6)); + break; +#endif + + default: + return EAFNOSUPPORT; + } + + return err; +} + + +int udp_multicast_join(struct udp_sock *us, const struct sa *group) +{ + return multicast_update(us, group, true); +} + + +int udp_multicast_leave(struct udp_sock *us, const struct sa *group) +{ + return multicast_update(us, group, false); +} diff --git a/src/udp/mod.mk b/src/udp/mod.mk index dbdcaf2..10cff50 100644 --- a/src/udp/mod.mk +++ b/src/udp/mod.mk @@ -5,3 +5,4 @@ # SRCS += udp/udp.c +SRCS += udp/mcast.c