xref: /freebsd/tests/sys/netinet/ip6_mrouted.c (revision 09e702ad40af0067017613070b42d72cbc2bec3a)
1*09e702adSMark Johnston /*
2*09e702adSMark Johnston  * Copyright (c) 2026 Stormshield
3*09e702adSMark Johnston  *
4*09e702adSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
5*09e702adSMark Johnston  */
6*09e702adSMark Johnston 
7*09e702adSMark Johnston /*
8*09e702adSMark Johnston  * A dead-simple IPv6 multicast routing daemon.  It registers itself with the
9*09e702adSMark Johnston  * multicast routing code and then waits for messages from the kernel.  Received
10*09e702adSMark Johnston  * messages are handled by installing multicast routes.
11*09e702adSMark Johnston  */
12*09e702adSMark Johnston 
13*09e702adSMark Johnston #include <sys/types.h>
14*09e702adSMark Johnston #include <sys/event.h>
15*09e702adSMark Johnston #include <sys/queue.h>
16*09e702adSMark Johnston #include <sys/socket.h>
17*09e702adSMark Johnston 
18*09e702adSMark Johnston #include <net/if.h>
19*09e702adSMark Johnston #include <netinet/in.h>
20*09e702adSMark Johnston #include <netinet6/ip6_mroute.h>
21*09e702adSMark Johnston #include <arpa/inet.h>
22*09e702adSMark Johnston 
23*09e702adSMark Johnston #include <assert.h>
24*09e702adSMark Johnston #include <err.h>
25*09e702adSMark Johnston #include <errno.h>
26*09e702adSMark Johnston #include <stdio.h>
27*09e702adSMark Johnston #include <stdlib.h>
28*09e702adSMark Johnston #include <string.h>
29*09e702adSMark Johnston #include <unistd.h>
30*09e702adSMark Johnston 
31*09e702adSMark Johnston struct mif {
32*09e702adSMark Johnston 	const char *name;
33*09e702adSMark Johnston 	int mifi;
34*09e702adSMark Johnston 	int pifi;
35*09e702adSMark Johnston 	STAILQ_ENTRY(mif) next;
36*09e702adSMark Johnston };
37*09e702adSMark Johnston static STAILQ_HEAD(, mif) miflist = STAILQ_HEAD_INITIALIZER(miflist);
38*09e702adSMark Johnston 
39*09e702adSMark Johnston static void *
xmalloc(size_t size)40*09e702adSMark Johnston xmalloc(size_t size)
41*09e702adSMark Johnston {
42*09e702adSMark Johnston 	void *ptr;
43*09e702adSMark Johnston 
44*09e702adSMark Johnston 	ptr = malloc(size);
45*09e702adSMark Johnston 	if (ptr == NULL)
46*09e702adSMark Johnston 		err(1, "malloc");
47*09e702adSMark Johnston 	return (ptr);
48*09e702adSMark Johnston }
49*09e702adSMark Johnston 
50*09e702adSMark Johnston static void
usage(void)51*09e702adSMark Johnston usage(void)
52*09e702adSMark Johnston {
53*09e702adSMark Johnston 	fprintf(stderr,
54*09e702adSMark Johnston 	    "usage: %s [-i <iface>] [-m <srcaddr>/<groupaddr>/<iface>]\n",
55*09e702adSMark Johnston 	    getprogname());
56*09e702adSMark Johnston 	exit(1);
57*09e702adSMark Johnston }
58*09e702adSMark Johnston 
59*09e702adSMark Johnston static void
add_route(int sd,const struct in6_addr * src,const struct in6_addr * group,mifi_t mifi)60*09e702adSMark Johnston add_route(int sd, const struct in6_addr *src, const struct in6_addr *group,
61*09e702adSMark Johnston     mifi_t mifi)
62*09e702adSMark Johnston {
63*09e702adSMark Johnston 	struct mf6cctl mfcc;
64*09e702adSMark Johnston 	struct mif *mif;
65*09e702adSMark Johnston 	int error;
66*09e702adSMark Johnston 
67*09e702adSMark Johnston 	memset(&mfcc, 0, sizeof(mfcc));
68*09e702adSMark Johnston 	mfcc.mf6cc_parent = mifi;
69*09e702adSMark Johnston 	mfcc.mf6cc_origin.sin6_family = AF_INET6;
70*09e702adSMark Johnston 	mfcc.mf6cc_origin.sin6_len = sizeof(struct sockaddr_in6);
71*09e702adSMark Johnston 	mfcc.mf6cc_origin.sin6_addr = *src;
72*09e702adSMark Johnston 	mfcc.mf6cc_mcastgrp.sin6_family = AF_INET6;
73*09e702adSMark Johnston 	mfcc.mf6cc_mcastgrp.sin6_len = sizeof(struct sockaddr_in6);
74*09e702adSMark Johnston 	mfcc.mf6cc_mcastgrp.sin6_addr = *group;
75*09e702adSMark Johnston 
76*09e702adSMark Johnston 	STAILQ_FOREACH(mif, &miflist, next) {
77*09e702adSMark Johnston 		if (mif->mifi != mifi)
78*09e702adSMark Johnston 			IF_SET(mif->mifi, &mfcc.mf6cc_ifset);
79*09e702adSMark Johnston 	}
80*09e702adSMark Johnston 
81*09e702adSMark Johnston 	error = setsockopt(sd, IPPROTO_IPV6, MRT6_ADD_MFC,
82*09e702adSMark Johnston 	    &mfcc, sizeof(mfcc));
83*09e702adSMark Johnston 	if (error != 0)
84*09e702adSMark Johnston 		err(1, "setsockopt(MRT6_ADD_MFC)");
85*09e702adSMark Johnston }
86*09e702adSMark Johnston 
87*09e702adSMark Johnston static void
handle_upcalls(int sd)88*09e702adSMark Johnston handle_upcalls(int sd)
89*09e702adSMark Johnston {
90*09e702adSMark Johnston 	struct kevent ev;
91*09e702adSMark Johnston 	int kq;
92*09e702adSMark Johnston 
93*09e702adSMark Johnston 	kq = kqueue();
94*09e702adSMark Johnston 	if (kq < 0)
95*09e702adSMark Johnston 		err(1, "kqueue");
96*09e702adSMark Johnston 	EV_SET(&ev, sd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
97*09e702adSMark Johnston 	if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0)
98*09e702adSMark Johnston 		err(1, "kevent");
99*09e702adSMark Johnston 
100*09e702adSMark Johnston 	for (;;) {
101*09e702adSMark Johnston 		char buf1[INET6_ADDRSTRLEN], buf2[INET6_ADDRSTRLEN];
102*09e702adSMark Johnston 		struct mrt6msg msg;
103*09e702adSMark Johnston 		ssize_t len;
104*09e702adSMark Johnston 		int n;
105*09e702adSMark Johnston 
106*09e702adSMark Johnston 		n = kevent(kq, NULL, 0, &ev, 1, NULL);
107*09e702adSMark Johnston 		if (n < 0) {
108*09e702adSMark Johnston 			if (errno == EINTR)
109*09e702adSMark Johnston 				break;
110*09e702adSMark Johnston 			err(1, "kevent");
111*09e702adSMark Johnston 		}
112*09e702adSMark Johnston 		if (n == 0)
113*09e702adSMark Johnston 			continue;
114*09e702adSMark Johnston 		assert(n == 1);
115*09e702adSMark Johnston 		assert(ev.filter == EVFILT_READ);
116*09e702adSMark Johnston 
117*09e702adSMark Johnston 		len = recv(sd, &msg, sizeof(msg), 0);
118*09e702adSMark Johnston 		if (len < 0)
119*09e702adSMark Johnston 			err(1, "recv");
120*09e702adSMark Johnston 		if ((size_t)len < sizeof(msg)) {
121*09e702adSMark Johnston 			warnx("short read on upcall, %zd bytes", len);
122*09e702adSMark Johnston 			continue;
123*09e702adSMark Johnston 		}
124*09e702adSMark Johnston 
125*09e702adSMark Johnston 		printf("upcall received:\n");
126*09e702adSMark Johnston 		printf("msgtype=%d mif=%d src=%s dst=%s\n",
127*09e702adSMark Johnston 		    msg.im6_msgtype, msg.im6_mif,
128*09e702adSMark Johnston 		    inet_ntop(AF_INET6, &msg.im6_src, buf1, sizeof(buf1)),
129*09e702adSMark Johnston 		    inet_ntop(AF_INET6, &msg.im6_dst, buf2, sizeof(buf2)));
130*09e702adSMark Johnston 
131*09e702adSMark Johnston 		add_route(sd, &msg.im6_src, &msg.im6_dst, msg.im6_mif);
132*09e702adSMark Johnston 	}
133*09e702adSMark Johnston 
134*09e702adSMark Johnston 	close(kq);
135*09e702adSMark Johnston }
136*09e702adSMark Johnston 
137*09e702adSMark Johnston int
main(int argc,char ** argv)138*09e702adSMark Johnston main(int argc, char **argv)
139*09e702adSMark Johnston {
140*09e702adSMark Johnston 	struct mif *mif;
141*09e702adSMark Johnston 	int ch, error, mifi, sd, v;
142*09e702adSMark Johnston 
143*09e702adSMark Johnston 	mifi = 0;
144*09e702adSMark Johnston 	while ((ch = getopt(argc, argv, "i:m:")) != -1) {
145*09e702adSMark Johnston 		switch (ch) {
146*09e702adSMark Johnston 		case 'i':
147*09e702adSMark Johnston 			mif = xmalloc(sizeof(*mif));
148*09e702adSMark Johnston 			mif->name = strdup(optarg);
149*09e702adSMark Johnston 			mif->mifi = mifi++;
150*09e702adSMark Johnston 			mif->pifi = if_nametoindex(optarg);
151*09e702adSMark Johnston 			if (mif->pifi == 0)
152*09e702adSMark Johnston 				errx(1, "unknown interface %s", optarg);
153*09e702adSMark Johnston 			STAILQ_INSERT_TAIL(&miflist, mif, next);
154*09e702adSMark Johnston 			break;
155*09e702adSMark Johnston 		default:
156*09e702adSMark Johnston 			usage();
157*09e702adSMark Johnston 			/* NOTREACHED */
158*09e702adSMark Johnston 		}
159*09e702adSMark Johnston 	}
160*09e702adSMark Johnston 	argc -= optind;
161*09e702adSMark Johnston 	argv += optind;
162*09e702adSMark Johnston 
163*09e702adSMark Johnston 	sd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
164*09e702adSMark Johnston 	if (sd < 0)
165*09e702adSMark Johnston 		err(1, "socket");
166*09e702adSMark Johnston 
167*09e702adSMark Johnston 	v = 1;
168*09e702adSMark Johnston 	error = setsockopt(sd, IPPROTO_IPV6, MRT6_INIT, &v, sizeof(v));
169*09e702adSMark Johnston 	if (error != 0)
170*09e702adSMark Johnston 		err(1, "setsockopt(MRT6_INIT)");
171*09e702adSMark Johnston 
172*09e702adSMark Johnston 	STAILQ_FOREACH(mif, &miflist, next) {
173*09e702adSMark Johnston 		struct mif6ctl mifc;
174*09e702adSMark Johnston 
175*09e702adSMark Johnston 		mifc.mif6c_mifi = mif->mifi;
176*09e702adSMark Johnston 		mifc.mif6c_pifi = mif->pifi;
177*09e702adSMark Johnston 		mifc.mif6c_flags = 0;
178*09e702adSMark Johnston 		error = setsockopt(sd, IPPROTO_IPV6, MRT6_ADD_MIF,
179*09e702adSMark Johnston 		    &mifc, sizeof(mifc));
180*09e702adSMark Johnston 		if (error != 0)
181*09e702adSMark Johnston 			err(1, "setsockopt(MRT6_ADD_MIF) on %s", mif->name);
182*09e702adSMark Johnston 	}
183*09e702adSMark Johnston 
184*09e702adSMark Johnston 	handle_upcalls(sd);
185*09e702adSMark Johnston 
186*09e702adSMark Johnston 	error = setsockopt(sd, IPPROTO_IPV6, MRT6_DONE, NULL, 0);
187*09e702adSMark Johnston 	if (error != 0)
188*09e702adSMark Johnston 		err(1, "setsockopt(MRT6_DONE)");
189*09e702adSMark Johnston 
190*09e702adSMark Johnston 	return (0);
191*09e702adSMark Johnston }
192