1/* $OpenBSD: mc6route.c,v 1.2 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 <net/if.h> 52#include <netinet/in.h> 53#include <netinet6/ip6_mroute.h> 54 55#include <err.h> 56#include <errno.h> 57#include <fcntl.h> 58#include <limits.h> 59#include <signal.h> 60#include <stdlib.h> 61#include <stdio.h> 62#include <string.h> 63#include <time.h> 64#include <unistd.h> 65 66void __dead usage(void); 67void sigexit(int); 68size_t get_sysctl(const int *mib, u_int mcnt, char **buf); 69 70void __dead 71usage(void) 72{ 73 fprintf(stderr, 74"mc6route [-b] [-f file] [-g group] -i ifname [-n timeout] -o outname\n" 75" [-r timeout]\n" 76" -b fork to background after setup\n" 77" -f file print message to log file, default stdout\n" 78" -g group multicast group, default 224.0.0.123\n" 79" -i ifname multicast interface address\n" 80" -n timeout expect not to receive any message until timeout\n" 81" -o outname outgoing interface address\n" 82" -r timeout receive timeout in seconds\n"); 83 exit(2); 84} 85 86int 87main(int argc, char *argv[]) 88{ 89 struct mif6ctl mif; 90 struct mf6cctl mfc; 91 struct mif6info *minfo; 92 FILE *log; 93 const char *errstr, *file, *group, *ifname, *outname; 94 char *buf; 95 size_t needed; 96 u_int64_t pktin, pktout; 97 int value, ch, s, fd, background, norecv; 98 unsigned int timeout; 99 pid_t pid; 100 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_MRTMIF }; 101 102 background = 0; 103 log = stdout; 104 file = NULL; 105 group = "ff04::123"; 106 ifname = NULL; 107 norecv = 0; 108 outname = NULL; 109 timeout = 0; 110 while ((ch = getopt(argc, argv, "bf:g:i:n:o:r:")) != -1) { 111 switch (ch) { 112 case 'b': 113 background = 1; 114 break; 115 case 'f': 116 file = optarg; 117 break; 118 case 'g': 119 group = optarg; 120 break; 121 case 'i': 122 ifname = optarg; 123 break; 124 case 'n': 125 norecv = 1; 126 timeout = strtonum(optarg, 1, INT_MAX, &errstr); 127 if (errstr != NULL) 128 errx(1, "no timeout is %s: %s", errstr, optarg); 129 break; 130 case 'o': 131 outname = optarg; 132 break; 133 case 'r': 134 timeout = strtonum(optarg, 1, INT_MAX, &errstr); 135 if (errstr != NULL) 136 errx(1, "timeout is %s: %s", errstr, optarg); 137 break; 138 default: 139 usage(); 140 } 141 } 142 argc -= optind; 143 argv += optind; 144 if (ifname == NULL) 145 errx(2, "no ifname"); 146 if (outname == NULL) 147 errx(2, "no outname"); 148 if (argc) 149 usage(); 150 151 if (file != NULL) { 152 log = fopen(file, "w"); 153 if (log == NULL) 154 err(1, "fopen %s", file); 155 } 156 157 s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 158 if (s == -1) 159 err(1, "socket"); 160 value = 1; 161 if (setsockopt(s, IPPROTO_IPV6, MRT6_INIT, &value, sizeof(value)) == -1) 162 err(1, "setsockopt MRT6_INIT"); 163 164 memset(&mif, 0, sizeof(mif)); 165 mif.mif6c_mifi = 0; 166 mif.mif6c_pifi = if_nametoindex(ifname); 167 if (mif.mif6c_pifi == 0) 168 err(1, "if_nametoindex %s", ifname); 169 if (setsockopt(s, IPPROTO_IPV6, MRT6_ADD_MIF, &mif, sizeof(mif)) == -1) 170 err(1, "setsockopt MRT6_ADD_MIF %s", ifname); 171 172 memset(&mif, 0, sizeof(mif)); 173 mif.mif6c_mifi = 1; 174 mif.mif6c_pifi = if_nametoindex(outname); 175 if (mif.mif6c_pifi == 0) 176 err(1, "if_nametoindex %s", outname); 177 if (setsockopt(s, IPPROTO_IPV6, MRT6_ADD_MIF, &mif, sizeof(mif)) == -1) 178 err(1, "setsockopt MRT6_ADD_MIF %s", outname); 179 180 memset(&mfc, 0, sizeof(mfc)); 181 if (inet_pton(AF_INET6, group, &mfc.mf6cc_mcastgrp.sin6_addr) == -1) 182 err(1, "inet_pton %s", group); 183 mfc.mf6cc_parent = 0; 184 IF_SET(1, &mfc.mf6cc_ifset); 185 186 if (setsockopt(s, IPPROTO_IPV6, MRT6_ADD_MFC, &mfc, sizeof(mfc)) == -1) 187 err(1, "setsockopt MRT6_ADD_MFC %s", ifname); 188 189 if (background) { 190 pid = fork(); 191 switch (pid) { 192 case -1: 193 err(1, "fork"); 194 case 0: 195 fd = open("/dev/null", O_RDWR); 196 if (fd == -1) 197 err(1, "open /dev/null"); 198 if (dup2(fd, 0) == -1) 199 err(1, "dup 0"); 200 if (dup2(fd, 1) == -1) 201 err(1, "dup 1"); 202 if (dup2(fd, 2) == -1) 203 err(1, "dup 2"); 204 break; 205 default: 206 _exit(0); 207 } 208 } 209 210 if (timeout) { 211 if (norecv) { 212 if (signal(SIGALRM, sigexit) == SIG_ERR) 213 err(1, "signal SIGALRM"); 214 } 215 alarm(timeout); 216 } 217 218 buf = NULL; 219 pktin = pktout = 0; 220 do { 221 struct timespec sleeptime = { 0, 10000000 }; 222 223 if (nanosleep(&sleeptime, NULL) == -1) 224 err(1, "nanosleep"); 225 needed = get_sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buf); 226 for (minfo = (struct mif6info *)buf; 227 (char *)minfo < buf + needed; 228 minfo++) { 229 switch (minfo->m6_ifindex) { 230 case 0: 231 if (pktin != minfo->m6_pkt_in) { 232 fprintf(log, "<<< %llu\n", 233 minfo->m6_pkt_in); 234 fflush(log); 235 } 236 pktin = minfo->m6_pkt_in; 237 break; 238 case 1: 239 if (pktout != minfo->m6_pkt_out) { 240 fprintf(log, ">>> %llu\n", 241 minfo->m6_pkt_out); 242 fflush(log); 243 } 244 pktout = minfo->m6_pkt_out; 245 break; 246 } 247 } 248 } while (pktin == 0 || pktout == 0); 249 free(buf); 250 251 if (norecv) 252 errx(1, "pktin %llu, pktout %llu", pktin, pktout); 253 254 return 0; 255} 256 257void 258sigexit(int sig) 259{ 260 _exit(0); 261} 262 263/* from netstat(8) */ 264size_t 265get_sysctl(const int *mib, u_int mcnt, char **buf) 266{ 267 size_t needed; 268 269 while (1) { 270 if (sysctl(mib, mcnt, NULL, &needed, NULL, 0) == -1) 271 err(1, "sysctl-estimate"); 272 if (needed == 0) 273 break; 274 if ((*buf = realloc(*buf, needed)) == NULL) 275 err(1, NULL); 276 if (sysctl(mib, mcnt, *buf, &needed, NULL, 0) == -1) { 277 if (errno == ENOMEM) 278 continue; 279 err(1, "sysctl"); 280 } 281 break; 282 } 283 284 return needed; 285} 286