1 /*- 2 * Copyright (c) 2007-2009 Bruce Simpson. 3 * Copyright (c) 2000 Wilbert De Graaf. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Diagnostic and test utility for IPv4 multicast sockets. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/types.h> 39 #include <sys/errno.h> 40 #include <sys/socket.h> 41 #include <sys/time.h> 42 #include <sys/ioctl.h> 43 44 #include <net/if.h> 45 #include <net/if_dl.h> 46 #include <net/ethernet.h> 47 #include <netinet/in.h> 48 49 #include <arpa/inet.h> 50 51 #include <stdlib.h> 52 #include <stdio.h> 53 #include <string.h> 54 #include <ctype.h> 55 #include <err.h> 56 #include <unistd.h> 57 58 static void process_file(char *, int); 59 static void process_cmd(char*, int, FILE *fp); 60 static void usage(void); 61 62 #define MAX_ADDRS 20 63 #define STR_SIZE 20 64 #define LINE_LENGTH 80 65 66 static int 67 inaddr_cmp(const void *a, const void *b) 68 { 69 return ((int)((const struct in_addr *)a)->s_addr - 70 ((const struct in_addr *)b)->s_addr); 71 } 72 73 int 74 main(int argc, char **argv) 75 { 76 char line[LINE_LENGTH]; 77 char *p; 78 int i, s; 79 80 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 81 if (s == -1) 82 err(1, "can't open socket"); 83 84 if (argc < 2) { 85 if (isatty(STDIN_FILENO)) { 86 printf("multicast membership test program; " 87 "enter ? for list of commands\n"); 88 } 89 do { 90 if (fgets(line, sizeof(line), stdin) != NULL) { 91 if (line[0] != 'f') 92 process_cmd(line, s, stdin); 93 else { 94 /* Get the filename */ 95 for (i = 1; isblank(line[i]); i++); 96 if ((p = (char*)strchr(line, '\n')) 97 != NULL) 98 *p = '\0'; 99 process_file(&line[i], s); 100 } 101 } 102 } while (!feof(stdin)); 103 } else { 104 for (i = 1; i < argc; i++) { 105 process_file(argv[i], s); 106 } 107 } 108 109 exit (0); 110 } 111 112 static void 113 process_file(char *fname, int s) 114 { 115 char line[80]; 116 FILE *fp; 117 char *lineptr; 118 119 fp = fopen(fname, "r"); 120 if (fp == NULL) { 121 warn("fopen"); 122 return; 123 } 124 125 /* Skip comments and empty lines. */ 126 while (fgets(line, sizeof(line), fp) != NULL) { 127 lineptr = line; 128 while (isblank(*lineptr)) 129 lineptr++; 130 if (*lineptr != '#' && *lineptr != '\n') 131 process_cmd(lineptr, s, fp); 132 } 133 134 fclose(fp); 135 } 136 137 static void 138 process_cmd(char *cmd, int s, FILE *fp __unused) 139 { 140 char str1[STR_SIZE]; 141 char str2[STR_SIZE]; 142 char str3[STR_SIZE]; 143 struct in_addr sources[MAX_ADDRS]; 144 struct ifreq ifr; 145 struct ip_mreq imr; 146 struct ip_mreq_source imrs; 147 char *line; 148 uint32_t fmode; 149 int i, n, opt, f, flags; 150 151 line = cmd; 152 while (isblank(*++line)) 153 ; /* Skip whitespace. */ 154 155 switch (*cmd) { 156 case '?': 157 usage(); 158 break; 159 160 case 'q': 161 close(s); 162 exit(0); 163 164 case 's': 165 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { 166 printf("-1\n"); 167 break; 168 } 169 sleep(n); 170 printf("ok\n"); 171 break; 172 173 case 'j': 174 case 'l': 175 str3[0] = '\0'; 176 sscanf(line, "%s %s %s", str1, str2, str3); 177 if ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) != 178 INADDR_NONE) { 179 /* 180 * inclusive mode join with source, possibly 181 * on existing membership. 182 */ 183 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) == 184 INADDR_NONE) || 185 ((imrs.imr_interface.s_addr = inet_addr(str2)) == 186 INADDR_NONE)) { 187 printf("-1\n"); 188 break; 189 } 190 opt = (*cmd == 'j') ? IP_ADD_SOURCE_MEMBERSHIP : 191 IP_DROP_SOURCE_MEMBERSHIP; 192 if (setsockopt( s, IPPROTO_IP, opt, &imrs, 193 sizeof(imrs)) != 0) { 194 warn("setsockopt %s", (*cmd == 'j') ? 195 "IP_ADD_SOURCE_MEMBERSHIP" : 196 "IP_DROP_SOURCE_MEMBERSHIP"); 197 } else { 198 printf("ok\n"); 199 } 200 } else { 201 /* exclusive mode join w/o source. */ 202 if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) == 203 INADDR_NONE) || 204 ((imr.imr_interface.s_addr = inet_addr(str2)) == 205 INADDR_NONE)) { 206 printf("-1\n"); 207 break; 208 } 209 opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP : 210 IP_DROP_MEMBERSHIP; 211 if (setsockopt( s, IPPROTO_IP, opt, &imr, 212 sizeof(imr)) != 0) { 213 warn("setsockopt %s", (*cmd == 'j') ? 214 "IP_ADD_MEMBERSHIP" : 215 "IP_DROP_MEMBERSHIP"); 216 } else { 217 printf("ok\n"); 218 } 219 } 220 break; 221 222 case 'a': 223 case 'd': { 224 struct sockaddr_dl *dlp; 225 struct ether_addr *ep; 226 227 memset(&ifr, 0, sizeof(struct ifreq)); 228 dlp = (struct sockaddr_dl *)&ifr.ifr_addr; 229 dlp->sdl_len = sizeof(struct sockaddr_dl); 230 dlp->sdl_family = AF_LINK; 231 dlp->sdl_index = 0; 232 dlp->sdl_nlen = 0; 233 dlp->sdl_alen = ETHER_ADDR_LEN; 234 dlp->sdl_slen = 0; 235 if (sscanf(line, "%s %s", str1, str2) != 2) { 236 warnc(EINVAL, "sscanf"); 237 break; 238 } 239 ep = ether_aton(str2); 240 if (ep == NULL) { 241 warnc(EINVAL, "ether_aton"); 242 break; 243 } 244 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE); 245 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); 246 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, 247 &ifr) == -1) 248 warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); 249 else 250 printf("ok\n"); 251 break; 252 } 253 254 case 'm': 255 printf("warning: IFF_ALLMULTI cannot be set from userland " 256 "in FreeBSD; command ignored.\n"); 257 break; 258 259 case 'p': 260 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { 261 printf("-1\n"); 262 break; 263 } 264 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { 265 warn("ioctl SIOCGIFFLAGS"); 266 break; 267 } 268 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 269 opt = IFF_PPROMISC; 270 if (f == 0) { 271 flags &= ~opt; 272 } else { 273 flags |= opt; 274 } 275 ifr.ifr_flags = flags & 0xffff; 276 ifr.ifr_flagshigh = flags >> 16; 277 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) 278 warn("ioctl SIOCGIFFLAGS"); 279 else 280 printf( "changed to 0x%08x\n", flags ); 281 break; 282 283 /* 284 * Set the socket to include or exclude filter mode, and 285 * add some sources to the filterlist, using the full-state, 286 * or advanced api. 287 */ 288 case 'i': 289 case 'e': 290 n = 0; 291 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; 292 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 293 printf("-1\n"); 294 break; 295 } 296 /* recycle imrs struct for convenience */ 297 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) == 298 INADDR_NONE) || 299 ((imrs.imr_interface.s_addr = inet_addr(str2)) == 300 INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) { 301 printf("-1\n"); 302 break; 303 } 304 for (i = 0; i < n; i++) { 305 fgets(str1, sizeof(str1), fp); 306 if ((sources[i].s_addr = inet_addr(str1)) == 307 INADDR_NONE) { 308 printf("-1\n"); 309 return; 310 } 311 } 312 if (setipv4sourcefilter(s, imrs.imr_interface, 313 imrs.imr_multiaddr, fmode, n, sources) != 0) 314 warn("getipv4sourcefilter"); 315 else 316 printf("ok\n"); 317 break; 318 319 /* 320 * Allow or block traffic from a source, using the 321 * delta based api. 322 */ 323 case 't': 324 case 'b': 325 sscanf(line, "%s %s %s", str1, str2, str3); 326 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) == 327 INADDR_NONE) || 328 ((imrs.imr_interface.s_addr = inet_addr(str2)) == 329 INADDR_NONE) || 330 ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) == 331 INADDR_NONE)) { 332 printf("-1\n"); 333 break; 334 } 335 /* First determine our current filter mode. */ 336 n = 0; 337 if (getipv4sourcefilter(s, imrs.imr_interface, 338 imrs.imr_multiaddr, &fmode, &n, NULL) != 0) { 339 warn("getipv4sourcefilter"); 340 break; 341 } 342 if (fmode == MCAST_EXCLUDE) { 343 /* Any source */ 344 opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE : 345 IP_BLOCK_SOURCE; 346 } else { 347 /* Controlled source */ 348 opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP : 349 IP_DROP_SOURCE_MEMBERSHIP; 350 } 351 if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1) 352 warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE"); 353 else 354 printf("ok\n"); 355 break; 356 357 case 'g': 358 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 359 printf("-1\n"); 360 break; 361 } 362 /* recycle imrs struct for convenience */ 363 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) == 364 INADDR_NONE) || 365 ((imrs.imr_interface.s_addr = inet_addr(str2)) == 366 INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) { 367 printf("-1\n"); 368 break; 369 } 370 if (getipv4sourcefilter(s, imrs.imr_interface, 371 imrs.imr_multiaddr, &fmode, &n, sources) != 0) { 372 warn("getipv4sourcefilter"); 373 break; 374 } 375 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : 376 "exclude"); 377 printf("%d\n", n); 378 qsort(sources, n, sizeof(struct in_addr), &inaddr_cmp); 379 for (i = 0; i < n; i++) 380 printf("%s\n", inet_ntoa(sources[i])); 381 break; 382 383 case '\n': 384 break; 385 default: 386 printf("invalid command\n"); 387 break; 388 } 389 } 390 391 static void 392 usage(void) 393 { 394 395 printf("j g.g.g.g i.i.i.i [s.s.s.s] - join IP multicast group\n"); 396 printf("l g.g.g.g i.i.i.i [s.s.s.s] - leave IP multicast group\n"); 397 printf("a ifname e.e.e.e.e.e - add ether multicast address\n"); 398 printf("d ifname e.e.e.e.e.e - delete ether multicast address\n"); 399 printf("m ifname 1/0 - set/clear ether allmulti flag\n"); 400 printf("p ifname 1/0 - set/clear ether promisc flag\n"); 401 printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n"); 402 printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n"); 403 printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n"); 404 printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n"); 405 printf("g g.g.g.g i.i.i.i n - get and show n src filters\n"); 406 printf("f filename - read command(s) from file\n"); 407 printf("s seconds - sleep for some time\n"); 408 printf("q - quit\n"); 409 } 410