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