1/* $OpenBSD: mcroute.c,v 1.3 2021/07/06 11:50:34 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 65void __dead usage(void); 66void sigexit(int); 67size_t get_sysctl(const int *mib, u_int mcnt, char **buf); 68 69void __dead 70usage(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 85int 86main(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 alarm(timeout); 213 } 214 215 buf = NULL; 216 pktin = pktout = 0; 217 do { 218 struct timespec sleeptime = { 0, 10000000 }; 219 220 if (nanosleep(&sleeptime, NULL) == -1) 221 err(1, "nanosleep"); 222 needed = get_sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buf); 223 for (vinfo = (struct vifinfo *)buf; 224 (char *)vinfo < buf + needed; 225 vinfo++) { 226 switch (vinfo->v_vifi) { 227 case 0: 228 if (pktin != vinfo->v_pkt_in) { 229 fprintf(log, "<<< %lu\n", 230 vinfo->v_pkt_in); 231 fflush(log); 232 } 233 pktin = vinfo->v_pkt_in; 234 break; 235 case 1: 236 if (pktout != vinfo->v_pkt_out) { 237 fprintf(log, ">>> %lu\n", 238 vinfo->v_pkt_out); 239 fflush(log); 240 } 241 pktout = vinfo->v_pkt_out; 242 break; 243 } 244 } 245 } while (pktin == 0 || pktout == 0); 246 free(buf); 247 248 if (norecv) 249 errx(1, "pktin %lu, pktout %lu", pktin, pktout); 250 251 return 0; 252} 253 254void 255sigexit(int sig) 256{ 257 _exit(0); 258} 259 260/* from netstat(8) */ 261size_t 262get_sysctl(const int *mib, u_int mcnt, char **buf) 263{ 264 size_t needed; 265 266 while (1) { 267 if (sysctl(mib, mcnt, NULL, &needed, NULL, 0) == -1) 268 err(1, "sysctl-estimate"); 269 if (needed == 0) 270 break; 271 if ((*buf = realloc(*buf, needed)) == NULL) 272 err(1, NULL); 273 if (sysctl(mib, mcnt, *buf, &needed, NULL, 0) == -1) { 274 if (errno == ENOMEM) 275 continue; 276 err(1, "sysctl"); 277 } 278 break; 279 } 280 281 return needed; 282} 283