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