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