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