xref: /openbsd-src/regress/sys/netinet/mcast/mcroute.c (revision 5a9255e7fde1eff660134511e5a28e2366fbe25a)
1*5a9255e7Sbluhm /*	$OpenBSD: mcroute.c,v 1.3 2021/07/06 11:50:34 bluhm Exp $	*/
2a631866dSbluhm /*
3a631866dSbluhm  * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
4a631866dSbluhm  *
5a631866dSbluhm  * Permission to use, copy, modify, and distribute this software for any
6a631866dSbluhm  * purpose with or without fee is hereby granted, provided that the above
7a631866dSbluhm  * copyright notice and this permission notice appear in all copies.
8a631866dSbluhm  *
9a631866dSbluhm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a631866dSbluhm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a631866dSbluhm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a631866dSbluhm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a631866dSbluhm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a631866dSbluhm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a631866dSbluhm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a631866dSbluhm  */
17a631866dSbluhm /*
18a631866dSbluhm  * Copyright (c) 1983, 1988, 1993
19a631866dSbluhm  *      The Regents of the University of California.  All rights reserved.
20a631866dSbluhm  *
21a631866dSbluhm  * Redistribution and use in source and binary forms, with or without
22a631866dSbluhm  * modification, are permitted provided that the following conditions
23a631866dSbluhm  * are met:
24a631866dSbluhm  * 1. Redistributions of source code must retain the above copyright
25a631866dSbluhm  *    notice, this list of conditions and the following disclaimer.
26a631866dSbluhm  * 2. Redistributions in binary form must reproduce the above copyright
27a631866dSbluhm  *    notice, this list of conditions and the following disclaimer in the
28a631866dSbluhm  *    documentation and/or other materials provided with the distribution.
29a631866dSbluhm  * 3. Neither the name of the University nor the names of its contributors
30a631866dSbluhm  *    may be used to endorse or promote products derived from this software
31a631866dSbluhm  *    without specific prior written permission.
32a631866dSbluhm  *
33a631866dSbluhm  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34a631866dSbluhm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35a631866dSbluhm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36a631866dSbluhm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37a631866dSbluhm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38a631866dSbluhm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39a631866dSbluhm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40a631866dSbluhm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41a631866dSbluhm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42a631866dSbluhm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43a631866dSbluhm  * SUCH DAMAGE.
44a631866dSbluhm  */
45a631866dSbluhm 
46a631866dSbluhm #include <sys/types.h>
47a631866dSbluhm #include <sys/socket.h>
48a631866dSbluhm #include <sys/sysctl.h>
49a631866dSbluhm 
50a631866dSbluhm #include <arpa/inet.h>
51a631866dSbluhm #include <netinet/in.h>
52a631866dSbluhm #include <netinet/ip_mroute.h>
53a631866dSbluhm 
54a631866dSbluhm #include <err.h>
55a631866dSbluhm #include <errno.h>
56a631866dSbluhm #include <fcntl.h>
57a631866dSbluhm #include <limits.h>
58a631866dSbluhm #include <signal.h>
59a631866dSbluhm #include <stdlib.h>
60a631866dSbluhm #include <stdio.h>
61a631866dSbluhm #include <string.h>
62a631866dSbluhm #include <time.h>
63a631866dSbluhm #include <unistd.h>
64a631866dSbluhm 
65a631866dSbluhm void __dead usage(void);
66a631866dSbluhm void sigexit(int);
67a631866dSbluhm size_t get_sysctl(const int *mib, u_int mcnt, char **buf);
68a631866dSbluhm 
69a631866dSbluhm void __dead
usage(void)70a631866dSbluhm usage(void)
71a631866dSbluhm {
72a631866dSbluhm 	fprintf(stderr,
73a631866dSbluhm "mcroute [-b] [-f file] [-g group] -i ifaddr [-n timeout] -o outaddr\n"
74a631866dSbluhm "    [-r timeout]\n"
75a631866dSbluhm "    -b              fork to background after setup\n"
76a631866dSbluhm "    -f file         print message to log file, default stdout\n"
77a631866dSbluhm "    -g group        multicast group, default 224.0.0.123\n"
78a631866dSbluhm "    -i ifaddr       multicast interface address\n"
79a631866dSbluhm "    -n timeout      expect not to receive any message until timeout\n"
80a631866dSbluhm "    -o outaddr      outgoing interface address\n"
81a631866dSbluhm "    -r timeout      receive timeout in seconds\n");
82a631866dSbluhm 	exit(2);
83a631866dSbluhm }
84a631866dSbluhm 
85a631866dSbluhm int
main(int argc,char * argv[])86a631866dSbluhm main(int argc, char *argv[])
87a631866dSbluhm {
88a631866dSbluhm 	struct vifctl vif;
89a631866dSbluhm 	struct mfcctl mfc;
90a631866dSbluhm 	struct vifinfo *vinfo;
91a631866dSbluhm 	FILE *log;
92a631866dSbluhm 	const char *errstr, *file, *group, *ifaddr, *outaddr;
93a631866dSbluhm 	char *buf;
94a631866dSbluhm 	size_t needed;
95a631866dSbluhm 	unsigned long pktin, pktout;
96a631866dSbluhm 	int value, ch, s, fd, background, norecv;
97a631866dSbluhm 	unsigned int timeout;
98a631866dSbluhm 	pid_t pid;
99a631866dSbluhm 	int mib[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_MRTVIF };
100a631866dSbluhm 
101a631866dSbluhm 	background = 0;
102a631866dSbluhm 	log = stdout;
103a631866dSbluhm 	file = NULL;
1049f2a3839Sbluhm 	group = "224.0.1.123";
105a631866dSbluhm 	ifaddr = NULL;
106a631866dSbluhm 	norecv = 0;
107a631866dSbluhm 	outaddr = NULL;
108a631866dSbluhm 	timeout = 0;
109a631866dSbluhm 	while ((ch = getopt(argc, argv, "bf:g:i:n:o:r:")) != -1) {
110a631866dSbluhm 		switch (ch) {
111a631866dSbluhm 		case 'b':
112a631866dSbluhm 			background = 1;
113a631866dSbluhm 			break;
114a631866dSbluhm 		case 'f':
115a631866dSbluhm 			file = optarg;
116a631866dSbluhm 			break;
117a631866dSbluhm 		case 'g':
118a631866dSbluhm 			group = optarg;
119a631866dSbluhm 			break;
120a631866dSbluhm 		case 'i':
121a631866dSbluhm 			ifaddr = optarg;
122a631866dSbluhm 			break;
123a631866dSbluhm 		case 'n':
124a631866dSbluhm 			norecv = 1;
125a631866dSbluhm 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
126a631866dSbluhm 			if (errstr != NULL)
127a631866dSbluhm 				errx(1, "no timeout is %s: %s", errstr, optarg);
128a631866dSbluhm 			break;
129a631866dSbluhm 		case 'o':
130a631866dSbluhm 			outaddr = optarg;
131a631866dSbluhm 			break;
132a631866dSbluhm 		case 'r':
133a631866dSbluhm 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
134a631866dSbluhm 			if (errstr != NULL)
135a631866dSbluhm 				errx(1, "timeout is %s: %s", errstr, optarg);
136a631866dSbluhm 			break;
137a631866dSbluhm 		default:
138a631866dSbluhm 			usage();
139a631866dSbluhm 		}
140a631866dSbluhm 	}
141a631866dSbluhm 	argc -= optind;
142a631866dSbluhm 	argv += optind;
143a631866dSbluhm 	if (ifaddr == NULL)
144a631866dSbluhm 		errx(2, "no ifaddr");
145a631866dSbluhm 	if (outaddr == NULL)
146a631866dSbluhm 		errx(2, "no outaddr");
147a631866dSbluhm 	if (argc)
148a631866dSbluhm 		usage();
149a631866dSbluhm 
150a631866dSbluhm 	if (file != NULL) {
151a631866dSbluhm 		log = fopen(file, "w");
152a631866dSbluhm 		if (log == NULL)
153a631866dSbluhm 			err(1, "fopen %s", file);
154a631866dSbluhm 	}
155a631866dSbluhm 
156a631866dSbluhm 	s = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
157a631866dSbluhm 	if (s == -1)
158a631866dSbluhm 		err(1, "socket");
159a631866dSbluhm 	value = 1;
160a631866dSbluhm 	if (setsockopt(s, IPPROTO_IP, MRT_INIT, &value, sizeof(value)) == -1)
161a631866dSbluhm 		err(1, "setsockopt MRT_INIT");
162a631866dSbluhm 
163a631866dSbluhm 	memset(&vif, 0, sizeof(vif));
164a631866dSbluhm 	vif.vifc_vifi = 0;
165a631866dSbluhm 	if (inet_pton(AF_INET, ifaddr, &vif.vifc_lcl_addr) == -1)
166a631866dSbluhm 		err(1, "inet_pton %s", ifaddr);
167a631866dSbluhm 	if (setsockopt(s, IPPROTO_IP, MRT_ADD_VIF, &vif, sizeof(vif)) == -1)
168a631866dSbluhm 		err(1, "setsockopt MRT_ADD_VIF %s", ifaddr);
169a631866dSbluhm 
170a631866dSbluhm 	memset(&vif, 0, sizeof(vif));
171a631866dSbluhm 	vif.vifc_vifi = 1;
172a631866dSbluhm 	if (inet_pton(AF_INET, outaddr, &vif.vifc_lcl_addr) == -1)
173a631866dSbluhm 		err(1, "inet_pton %s", outaddr);
174a631866dSbluhm 	if (setsockopt(s, IPPROTO_IP, MRT_ADD_VIF, &vif, sizeof(vif)) == -1)
175a631866dSbluhm 		err(1, "setsockopt MRT_ADD_VIF %s", outaddr);
176a631866dSbluhm 
177a631866dSbluhm 	memset(&mfc, 0, sizeof(mfc));
178a631866dSbluhm 	if (inet_pton(AF_INET, group, &mfc.mfcc_mcastgrp) == -1)
179a631866dSbluhm 		err(1, "inet_pton %s", group);
180a631866dSbluhm 	mfc.mfcc_parent = 0;
181a631866dSbluhm 	mfc.mfcc_ttls[1] = 1;
182a631866dSbluhm 
183a631866dSbluhm 	if (setsockopt(s, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)) == -1)
1849f2a3839Sbluhm 		err(1, "setsockopt MRT_ADD_MFC %s", group);
185a631866dSbluhm 
186a631866dSbluhm 	if (background) {
187a631866dSbluhm 		pid = fork();
188a631866dSbluhm 		switch (pid) {
189a631866dSbluhm 		case -1:
190a631866dSbluhm 			err(1, "fork");
191a631866dSbluhm 		case 0:
192a631866dSbluhm 			fd = open("/dev/null", O_RDWR);
193a631866dSbluhm 			if (fd == -1)
194a631866dSbluhm 				err(1, "open /dev/null");
195a631866dSbluhm 			if (dup2(fd, 0) == -1)
196a631866dSbluhm 				err(1, "dup 0");
197a631866dSbluhm 			if (dup2(fd, 1) == -1)
198a631866dSbluhm 				err(1, "dup 1");
199a631866dSbluhm 			if (dup2(fd, 2) == -1)
200a631866dSbluhm 				err(1, "dup 2");
201a631866dSbluhm 			break;
202a631866dSbluhm 		default:
203a631866dSbluhm 			_exit(0);
204a631866dSbluhm 		}
205a631866dSbluhm 	}
206a631866dSbluhm 
207a631866dSbluhm 	if (timeout) {
208a631866dSbluhm 		if (norecv) {
209a631866dSbluhm 			if (signal(SIGALRM, sigexit) == SIG_ERR)
210a631866dSbluhm 				err(1, "signal SIGALRM");
211a631866dSbluhm 		}
212*5a9255e7Sbluhm 		alarm(timeout);
213a631866dSbluhm 	}
214a631866dSbluhm 
215a631866dSbluhm 	buf = NULL;
216a631866dSbluhm 	pktin = pktout = 0;
217a631866dSbluhm 	do {
218a631866dSbluhm 		struct timespec sleeptime = { 0, 10000000 };
219a631866dSbluhm 
220a631866dSbluhm 		if (nanosleep(&sleeptime, NULL) == -1)
221a631866dSbluhm 			err(1, "nanosleep");
222a631866dSbluhm 		needed = get_sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buf);
223a631866dSbluhm 		for (vinfo = (struct vifinfo *)buf;
224a631866dSbluhm 		    (char *)vinfo < buf + needed;
225a631866dSbluhm 		    vinfo++) {
226a631866dSbluhm 			switch (vinfo->v_vifi) {
227a631866dSbluhm 			case 0:
228a631866dSbluhm 				if (pktin != vinfo->v_pkt_in) {
229a631866dSbluhm 					fprintf(log, "<<< %lu\n",
230a631866dSbluhm 					    vinfo->v_pkt_in);
231a631866dSbluhm 					fflush(log);
232a631866dSbluhm 				}
233a631866dSbluhm 				pktin = vinfo->v_pkt_in;
234a631866dSbluhm 				break;
235a631866dSbluhm 			case 1:
236a631866dSbluhm 				if (pktout != vinfo->v_pkt_out) {
237a631866dSbluhm 					fprintf(log, ">>> %lu\n",
238a631866dSbluhm 					    vinfo->v_pkt_out);
239a631866dSbluhm 					fflush(log);
240a631866dSbluhm 				}
241a631866dSbluhm 				pktout = vinfo->v_pkt_out;
242a631866dSbluhm 				break;
243a631866dSbluhm 			}
244a631866dSbluhm 		}
245a631866dSbluhm 	} while (pktin == 0 || pktout == 0);
246a631866dSbluhm 	free(buf);
247a631866dSbluhm 
248a631866dSbluhm 	if (norecv)
249a631866dSbluhm 		errx(1, "pktin %lu, pktout %lu", pktin, pktout);
250a631866dSbluhm 
251a631866dSbluhm 	return 0;
252a631866dSbluhm }
253a631866dSbluhm 
254a631866dSbluhm void
sigexit(int sig)255a631866dSbluhm sigexit(int sig)
256a631866dSbluhm {
257a631866dSbluhm 	_exit(0);
258a631866dSbluhm }
259a631866dSbluhm 
260a631866dSbluhm /* from netstat(8) */
261a631866dSbluhm size_t
get_sysctl(const int * mib,u_int mcnt,char ** buf)262a631866dSbluhm get_sysctl(const int *mib, u_int mcnt, char **buf)
263a631866dSbluhm {
264a631866dSbluhm 	size_t needed;
265a631866dSbluhm 
266a631866dSbluhm 	while (1) {
267a631866dSbluhm 		if (sysctl(mib, mcnt, NULL, &needed, NULL, 0) == -1)
268a631866dSbluhm 			err(1, "sysctl-estimate");
269a631866dSbluhm 		if (needed == 0)
270a631866dSbluhm 			break;
271a631866dSbluhm 		if ((*buf = realloc(*buf, needed)) == NULL)
272a631866dSbluhm 			err(1, NULL);
273a631866dSbluhm 		if (sysctl(mib, mcnt, *buf, &needed, NULL, 0) == -1) {
274a631866dSbluhm 			if (errno == ENOMEM)
275a631866dSbluhm 				continue;
276a631866dSbluhm 			err(1, "sysctl");
277a631866dSbluhm 		}
278a631866dSbluhm 		break;
279a631866dSbluhm 	}
280a631866dSbluhm 
281a631866dSbluhm 	return needed;
282a631866dSbluhm }
283