Skip to content

Commit

Permalink
Implement IPv6 multicast.
Browse files Browse the repository at this point in the history
  • Loading branch information
bluhm committed Jan 1, 2025
1 parent 485752c commit ea39bca
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 55 deletions.
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ test-mcast:
wait $$!
grep -q 'sockname: 224.0.0.123 ' out

TEST += mcast6
test-mcast6:
@echo '\n==== $@ ===='
./udpbench -Ilo0 -p0 -t3 recv ff04::123 | tee out & \
sleep 1; \
port=`awk '/^sockname:/{print $$3}' out`; \
./udpbench -Ilo0 -L1 -T0 -p$$port -t1 \
send ff04::123 || exit 1; \
wait $$!
grep -q 'sockname: ff04::123 ' out

TEST += mcast-repeat
test-mcast-repeat:
@echo '\n==== $@ ===='
Expand All @@ -86,6 +97,18 @@ test-mcast-repeat:
grep -q 'sockname: 224.0.0.123 ' out
grep -q 'sockname: 224.0.0.124 ' out

TEST += mcast6-repeat
test-mcast6-repeat:
@echo '\n==== $@ ===='
./udpbench -N2 -Ilo0 -p0 -t3 recv ff04::123 | tee out & \
sleep 1; \
port=`awk '/^sockname:/{print $$3}' out | sort -u`; \
./udpbench -N2 -Ilo0 -L1 -T0 -p$$port -t1 \
send ff04::123 || exit 1; \
wait $$!
grep -q 'sockname: ff04::123 ' out
grep -q 'sockname: ff04::124 ' out

.PHONY: ${TEST:S/^/test-/}
test: ${TEST:S/^/test-/}

Expand Down
186 changes: 131 additions & 55 deletions udpbench.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
*/

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
Expand Down Expand Up @@ -54,13 +56,13 @@ void udp_bind_receive(struct timeval *, struct timeval *, struct timeval *);
void udp_socket_fork(int *,
int(*)(int, struct sockaddr *, socklen_t *),
int(*)(int, const struct sockaddr *, socklen_t),
void(*)(int, const struct sockaddr_in *));
void(*)(int, const struct sockaddr *));
int udp_socket_wait(int, pid_t, FILE *);
void alarm_handler(int);
int udp_bind(int *, const char *, const char *);
int udp_connect(int *, const char *, const char *);
void multicast_membership(int, const struct sockaddr_in *);
void multicast_interface(int, const struct sockaddr_in *);
void multicast_membership(int, const struct sockaddr *);
void multicast_interface(int, const struct sockaddr *);
void udp_getsockname(int, char *, char *);
void udp_setbuffersize(int, int, int);
void udp_setrouteralert(int);
Expand Down Expand Up @@ -433,7 +435,7 @@ void
udp_socket_fork(int *udp_socket,
int(*getname)(int, struct sockaddr *, socklen_t *),
int(*setname)(int, const struct sockaddr *, socklen_t),
void(*multicast)(int, const struct sockaddr_in *))
void(*multicast)(int, const struct sockaddr *))
{
char localaddr[NI_MAXHOST], localport[NI_MAXSERV];
struct sockaddr_storage ss;
Expand Down Expand Up @@ -470,14 +472,16 @@ udp_socket_fork(int *udp_socket,
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&ss;
((uint8_t *)&sin6->sin6_addr.s6_addr)[15]++;
if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
multicast = NULL;
break;
}
*udp_socket = socket(ss.ss_family, SOCK_DGRAM,
IPPROTO_UDP);
if (*udp_socket == -1)
err(1, "socket %d", n);
if (multicast)
multicast(*udp_socket, (struct sockaddr_in *)&ss);
multicast(*udp_socket, (struct sockaddr *)&ss);
if (setname(*udp_socket, (struct sockaddr *)&ss, sslen)
== -1)
err(1, "setname %d", n);
Expand Down Expand Up @@ -553,6 +557,9 @@ udp_bind(int *udp_family, const char *host, const char *serv)
errx(1, "getaddrinfo: %s", gai_strerror(error));
udp_socket = -1;
for (res = res0; res; res = res->ai_next) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;

if (divert) {
/* pf divert packet uses raw socket */
res->ai_socktype = SOCK_RAW;
Expand All @@ -568,34 +575,35 @@ udp_bind(int *udp_family, const char *host, const char *serv)
cause = "socket";
continue;
}

if (res->ai_family == AF_INET) {
const struct sockaddr_in *sin =
(const struct sockaddr_in *)res->ai_addr;

switch (res->ai_family) {
case AF_INET:
sin = (struct sockaddr_in *)res->ai_addr;
if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
multicast_membership(udp_socket, sin);
multicast_membership(udp_socket, res->ai_addr);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)res->ai_addr;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
multicast_membership(udp_socket, res->ai_addr);
break;
}

if (divert) {
/* divert packet socket is bound to port only */
if (res->ai_family == AF_INET) {
struct sockaddr_in *sin;

switch (res->ai_family) {
case AF_INET:
sin = (struct sockaddr_in *)res->ai_addr;
memset(&sin->sin_addr, 0,
sizeof(struct in_addr));
if (sin->sin_port == 0)
errx(1, "divert needs bind port");
}
if (res->ai_family == AF_INET) {
struct sockaddr_in6 *sin6;

break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)res->ai_addr;
memset(&sin6->sin6_addr, 0,
sizeof(struct in6_addr));
if (sin6->sin6_port == 0)
errx(1, "divert needs bind port");
break;
}
}
if (bind(udp_socket, res->ai_addr, res->ai_addrlen) == -1) {
Expand Down Expand Up @@ -633,21 +641,27 @@ udp_connect(int *udp_family, const char *host, const char *serv)
errx(1, "getaddrinfo: %s", gai_strerror(error));
udp_socket = -1;
for (res = res0; res; res = res->ai_next) {
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;

udp_socket = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (udp_socket == -1) {
cause = "socket";
continue;
}

if (res->ai_family == AF_INET) {
const struct sockaddr_in *sin =
(const struct sockaddr_in *)res->ai_addr;

switch (res->ai_family) {
case AF_INET:
sin = (struct sockaddr_in *)res->ai_addr;
if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
multicast_interface(udp_socket, sin);
multicast_interface(udp_socket, res->ai_addr);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)res->ai_addr;
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
multicast_interface(udp_socket, res->ai_addr);
break;
}

if (connect(udp_socket, res->ai_addr, res->ai_addrlen) == -1) {
cause = "connect";
save_errno = errno;
Expand All @@ -667,50 +681,112 @@ udp_connect(int *udp_family, const char *host, const char *serv)
}

void
multicast_membership(int udp_socket, const struct sockaddr_in *sin)
multicast_membership(int udp_socket, const struct sockaddr *sa)
{
const struct sockaddr_in *sin;
const struct sockaddr_in6 *sin6;
struct in_addr addr;
struct ip_mreq mreq;
struct ipv6_mreq mreq6;
unsigned int ifidx;

if (strcmp(mcastifaddr, "none") == 0)
errx(1, "multicast send needs interface address");
if (inet_pton(AF_INET, mcastifaddr, &addr) == -1)
errx(1, "inet_pton '%s'", mcastifaddr);

mreq.imr_multiaddr = sin->sin_addr;
mreq.imr_interface = addr;
if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) == -1) {
err(1, "setsockopt IP_ADD_MEMBERSHIP %s", mcastifaddr);
errx(1, "multicast recv needs interface address");

switch (sa->sa_family) {
case AF_INET:
sin = (struct sockaddr_in *)sa;
if (inet_pton(AF_INET, mcastifaddr, &addr) == -1)
errx(1, "inet_pton '%s'", mcastifaddr);

mreq.imr_multiaddr = sin->sin_addr;
mreq.imr_interface = addr;
if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) == -1) {
err(1, "setsockopt IP_ADD_MEMBERSHIP %s", mcastifaddr);
}
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)sa;
ifidx = if_nametoindex(mcastifaddr);
if (ifidx == 0)
errx(1, "if_nametoindex '%s'", mcastifaddr);

mreq6.ipv6mr_multiaddr = sin6->sin6_addr;
mreq6.ipv6mr_interface = ifidx;
if (setsockopt(udp_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq6, sizeof(mreq6)) == -1) {
err(1, "setsockopt IPV6_JOIN_GROUP %s", mcastifaddr);
}
break;
}
}

void
multicast_interface(int udp_socket, const struct sockaddr_in *sin)
multicast_interface(int udp_socket, const struct sockaddr *sa)
{
struct in_addr addr;
unsigned int ifidx;

if (strcmp(mcastifaddr, "none") == 0)
errx(1, "multicast send needs interface address");
if (inet_pton(AF_INET, mcastifaddr, &addr) == -1)
errx(1, "inet_pton ifaddr '%s'", mcastifaddr);

if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF,
&addr, sizeof(addr)) == -1)
err(1, "setsockopt IP_MULTICAST_IF '%s'", mcastifaddr);
if (mcastloop != -1) {
unsigned char value = mcastloop;

if (setsockopt(udp_socket, IPPROTO_IP,
IP_MULTICAST_LOOP, &value, sizeof(value)) == -1)
err(1, "setsockopt IP_MULTICAST_LOOP %d", mcastloop);
}
if (mcastttl != -1) {
unsigned char value = mcastttl;

if (setsockopt(udp_socket, IPPROTO_IP,
IP_MULTICAST_TTL, &value, sizeof(value)) == -1)
err(1, "setsockopt IP_MULTICAST_TTL %d", mcastttl);
switch (sa->sa_family) {
case AF_INET:
if (inet_pton(AF_INET, mcastifaddr, &addr) == -1)
errx(1, "inet_pton ifaddr '%s'", mcastifaddr);

if (setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF,
&addr, sizeof(addr)) == -1) {
err(1, "setsockopt IP_MULTICAST_IF '%s'", mcastifaddr);
}
if (mcastloop != -1) {
unsigned char value = mcastloop;

if (setsockopt(udp_socket, IPPROTO_IP,
IP_MULTICAST_LOOP, &value, sizeof(value)) == -1) {
err(1, "setsockopt IP_MULTICAST_LOOP %d",
mcastloop);
}
}
if (mcastttl != -1) {
unsigned char value = mcastttl;

if (setsockopt(udp_socket, IPPROTO_IP,
IP_MULTICAST_TTL, &value, sizeof(value)) == -1) {
err(1, "setsockopt IP_MULTICAST_TTL %d",
mcastttl);
}
}
break;
case AF_INET6:
ifidx = if_nametoindex(mcastifaddr);
if (ifidx == 0)
err(1, "if_nametoindex %s", mcastifaddr);
if (setsockopt(udp_socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
&ifidx, sizeof(ifidx)) == -1)
err(1, "setsockopt IPV6_MULTICAST_IF %s", mcastifaddr);

if (mcastloop != -1) {
unsigned int value = mcastloop;

if (setsockopt(udp_socket, IPPROTO_IPV6,
IPV6_MULTICAST_LOOP, &value, sizeof(value)) == -1) {
err(1, "setsockopt IPV6_MULTICAST_LOOP %d",
mcastloop);
}
}
if (mcastttl != -1) {
unsigned int value = mcastttl;

if (setsockopt(udp_socket, IPPROTO_IPV6,
IPV6_MULTICAST_HOPS, &value, sizeof(value)) == -1) {
err(1, "setsockopt IPV6_MULTICAST_HOPS %d",
mcastttl);
}
}

break;
}
}

Expand Down

0 comments on commit ea39bca

Please sign in to comment.