1 /* $NetBSD: ifwatchd.c,v 1.43 2018/03/07 10:06:41 roy Exp $ */ 2 #include <sys/cdefs.h> 3 __RCSID("$NetBSD: ifwatchd.c,v 1.43 2018/03/07 10:06:41 roy Exp $"); 4 5 /*- 6 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Martin Husemann <martin@NetBSD.org>. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/param.h> 36 #include <sys/ioctl.h> 37 #include <sys/socket.h> 38 #include <sys/queue.h> 39 #include <sys/wait.h> 40 #include <net/if.h> 41 #include <net/if_dl.h> 42 #include <net/if_media.h> 43 #include <net/route.h> 44 #include <netinet/in.h> 45 #include <netinet/in_var.h> 46 #include <arpa/inet.h> 47 48 #include <paths.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <netdb.h> 54 #include <err.h> 55 #include <ifaddrs.h> 56 #include <syslog.h> 57 58 enum event { ARRIVAL, DEPARTURE, UP, DOWN, CARRIER, NO_CARRIER }; 59 enum addrflag { NOTREADY, DETACHED, READY }; 60 61 /* local functions */ 62 __dead static void usage(void); 63 static void dispatch(const void *, size_t); 64 static enum addrflag check_addrflags(int af, int addrflags); 65 static void check_addrs(const struct ifa_msghdr *ifam); 66 static void invoke_script(const char *ifname, enum event ev, 67 const struct sockaddr *sa, const struct sockaddr *dst); 68 static void list_interfaces(const char *ifnames); 69 static void check_announce(const struct if_announcemsghdr *ifan); 70 static void check_carrier(const struct if_msghdr *ifm); 71 static void free_interfaces(void); 72 static struct interface_data * find_interface(int index); 73 static void run_initial_ups(void); 74 75 /* global variables */ 76 static int verbose = 0, quiet = 0; 77 static int inhibit_initial = 0; 78 static const char *arrival_script = NULL; 79 static const char *departure_script = NULL; 80 static const char *up_script = NULL; 81 static const char *down_script = NULL; 82 static const char *carrier_script = NULL; 83 static const char *no_carrier_script = NULL; 84 static const char DummyTTY[] = _PATH_DEVNULL; 85 static const char DummySpeed[] = "9600"; 86 static const char **scripts[] = { 87 &arrival_script, 88 &departure_script, 89 &up_script, 90 &down_script, 91 &carrier_script, 92 &no_carrier_script 93 }; 94 95 struct interface_data { 96 SLIST_ENTRY(interface_data) next; 97 int index; 98 int last_carrier_status; 99 char * ifname; 100 }; 101 static SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); 102 103 int 104 main(int argc, char **argv) 105 { 106 int c, s, n; 107 int errs = 0; 108 struct msghdr msg; 109 struct iovec iov[1]; 110 char buf[2048]; 111 unsigned char msgfilter[] = { 112 RTM_IFINFO, RTM_IFANNOUNCE, 113 RTM_NEWADDR, RTM_DELADDR, 114 }; 115 116 openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON); 117 while ((c = getopt(argc, argv, "qvhic:n:u:d:A:D:")) != -1) { 118 switch (c) { 119 case 'h': 120 usage(); 121 return 0; 122 123 case 'i': 124 inhibit_initial = 1; 125 break; 126 127 case 'v': 128 verbose++; 129 break; 130 131 case 'q': 132 quiet = 1; 133 break; 134 135 case 'c': 136 carrier_script = optarg; 137 break; 138 139 case 'n': 140 no_carrier_script = optarg; 141 break; 142 143 case 'u': 144 up_script = optarg; 145 break; 146 147 case 'd': 148 down_script = optarg; 149 break; 150 151 case 'A': 152 arrival_script = optarg; 153 break; 154 155 case 'D': 156 departure_script = optarg; 157 break; 158 159 default: 160 errs++; 161 break; 162 } 163 } 164 165 if (errs) 166 usage(); 167 168 argv += optind; 169 argc -= optind; 170 171 if (argc <= 0) 172 usage(); 173 174 if (verbose) { 175 printf("up_script: %s\ndown_script: %s\n", 176 up_script, down_script); 177 printf("arrival_script: %s\ndeparture_script: %s\n", 178 arrival_script, departure_script); 179 printf("carrier_script: %s\nno_carrier_script: %s\n", 180 carrier_script, no_carrier_script); 181 printf("verbosity = %d\n", verbose); 182 } 183 184 while (argc > 0) { 185 list_interfaces(argv[0]); 186 argv++; 187 argc--; 188 } 189 190 if (!verbose) 191 daemon(0, 0); 192 193 s = socket(PF_ROUTE, SOCK_RAW, 0); 194 if (s < 0) { 195 syslog(LOG_ERR, "error opening routing socket: %m"); 196 exit(EXIT_FAILURE); 197 } 198 if (setsockopt(s, PF_ROUTE, RO_MSGFILTER, 199 &msgfilter, sizeof(msgfilter)) < 0) 200 syslog(LOG_ERR, "RO_MSGFILTER: %m"); 201 202 if (!inhibit_initial) 203 run_initial_ups(); 204 205 iov[0].iov_base = buf; 206 iov[0].iov_len = sizeof(buf); 207 memset(&msg, 0, sizeof(msg)); 208 msg.msg_iov = iov; 209 msg.msg_iovlen = 1; 210 211 for (;;) { 212 n = recvmsg(s, &msg, 0); 213 if (n == -1) { 214 syslog(LOG_ERR, "recvmsg: %m"); 215 exit(EXIT_FAILURE); 216 } 217 if (n != 0) 218 dispatch(iov[0].iov_base, n); 219 } 220 221 close(s); 222 free_interfaces(); 223 closelog(); 224 225 return EXIT_SUCCESS; 226 } 227 228 static void 229 usage(void) 230 { 231 fprintf(stderr, 232 "usage:\n" 233 "\tifwatchd [-hiqv] [-A arrival-script] [-D departure-script]\n" 234 "\t\t [-d down-script] [-u up-script]\n" 235 "\t\t [-c carrier-script] [-n no-carrier-script] ifname(s)\n" 236 "\twhere:\n" 237 "\t -A <cmd> specify command to run on interface arrival event\n" 238 "\t -c <cmd> specify command to run on interface carrier-detect event\n" 239 "\t -D <cmd> specify command to run on interface departure event\n" 240 "\t -d <cmd> specify command to run on interface down event\n" 241 "\t -n <cmd> specify command to run on interface no-carrier-detect event\n" 242 "\t -h show this help message\n" 243 "\t -i no (!) initial run of the up script if the interface\n" 244 "\t is already up on ifwatchd startup\n" 245 "\t -q quiet mode, don't syslog informational messages\n" 246 "\t -u <cmd> specify command to run on interface up event\n" 247 "\t -v verbose/debug output, don't run in background\n"); 248 exit(EXIT_FAILURE); 249 } 250 251 static void 252 dispatch(const void *msg, size_t len) 253 { 254 const struct rt_msghdr *hd = msg; 255 256 if (hd->rtm_version != RTM_VERSION) 257 return; 258 259 switch (hd->rtm_type) { 260 case RTM_NEWADDR: 261 case RTM_DELADDR: 262 check_addrs(msg); 263 break; 264 case RTM_IFANNOUNCE: 265 check_announce(msg); 266 break; 267 case RTM_IFINFO: 268 check_carrier(msg); 269 break; 270 default: 271 /* Should be impossible as we filter messages. */ 272 if (verbose) 273 printf("unknown message ignored (%d)\n", hd->rtm_type); 274 break; 275 } 276 } 277 278 static enum addrflag 279 check_addrflags(int af, int addrflags) 280 { 281 282 switch (af) { 283 case AF_INET: 284 if (addrflags & IN_IFF_NOTREADY) 285 return NOTREADY; 286 if (addrflags & IN_IFF_DETACHED) 287 return DETACHED; 288 break; 289 case AF_INET6: 290 if (addrflags & IN6_IFF_NOTREADY) 291 return NOTREADY; 292 if (addrflags & IN6_IFF_DETACHED) 293 return DETACHED; 294 break; 295 } 296 return READY; 297 } 298 299 static void 300 check_addrs(const struct ifa_msghdr *ifam) 301 { 302 const char *cp = (const char *)(ifam + 1); 303 const struct sockaddr *sa, *ifa = NULL, *brd = NULL; 304 unsigned i; 305 struct interface_data *ifd = NULL; 306 int aflag; 307 enum event ev; 308 309 if (ifam->ifam_addrs == 0) 310 return; 311 for (i = 1; i; i <<= 1) { 312 if ((i & ifam->ifam_addrs) == 0) 313 continue; 314 sa = (const struct sockaddr *)cp; 315 if (i == RTA_IFP) { 316 const struct sockaddr_dl *li; 317 318 li = (const struct sockaddr_dl *)sa; 319 if ((ifd = find_interface(li->sdl_index)) == NULL) { 320 if (verbose) 321 printf("ignoring change" 322 " on interface #%d\n", 323 li->sdl_index); 324 return; 325 } 326 } else if (i == RTA_IFA) 327 ifa = sa; 328 else if (i == RTA_BRD) 329 brd = sa; 330 RT_ADVANCE(cp, sa); 331 } 332 if (ifa != NULL && ifd != NULL) { 333 ev = ifam->ifam_type == RTM_DELADDR ? DOWN : UP; 334 aflag = check_addrflags(ifa->sa_family, ifam->ifam_addrflags); 335 if ((ev == UP && aflag == READY) || ev == DOWN) 336 invoke_script(ifd->ifname, ev, ifa, brd); 337 } 338 } 339 340 static void 341 invoke_script(const char *ifname, enum event ev, 342 const struct sockaddr *sa, const struct sockaddr *dest) 343 { 344 char addr[NI_MAXHOST], daddr[NI_MAXHOST]; 345 const char *script; 346 int status; 347 348 if (ifname == NULL) 349 return; 350 351 script = *scripts[ev]; 352 if (script == NULL) 353 return; 354 355 addr[0] = daddr[0] = 0; 356 if (sa != NULL) { 357 const struct sockaddr_in *sin; 358 const struct sockaddr_in6 *sin6; 359 360 if (sa->sa_len == 0) { 361 syslog(LOG_ERR, 362 "illegal socket address (sa_len == 0)"); 363 return; 364 } 365 switch (sa->sa_family) { 366 case AF_INET: 367 sin = (const struct sockaddr_in *)sa; 368 if (sin->sin_addr.s_addr == INADDR_ANY || 369 sin->sin_addr.s_addr == INADDR_BROADCAST) 370 return; 371 break; 372 case AF_INET6: 373 sin6 = (const struct sockaddr_in6 *)sa; 374 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 375 return; 376 break; 377 default: 378 break; 379 } 380 381 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, 382 NI_NUMERICHOST)) { 383 if (verbose) 384 printf("getnameinfo failed\n"); 385 return; /* this address can not be handled */ 386 } 387 } 388 389 if (dest != NULL) { 390 if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr, 391 NULL, 0, NI_NUMERICHOST)) { 392 if (verbose) 393 printf("getnameinfo failed\n"); 394 return; /* this address can not be handled */ 395 } 396 } 397 398 if (verbose) 399 (void) printf("calling: %s %s %s %s %s %s\n", 400 script, ifname, DummyTTY, DummySpeed, addr, daddr); 401 if (!quiet) 402 syslog(LOG_INFO, "calling: %s %s %s %s %s %s\n", 403 script, ifname, DummyTTY, DummySpeed, addr, daddr); 404 405 switch (vfork()) { 406 case -1: 407 syslog(LOG_ERR, "cannot fork: %m"); 408 break; 409 case 0: 410 if (execl(script, script, ifname, DummyTTY, DummySpeed, 411 addr, daddr, NULL) == -1) { 412 syslog(LOG_ERR, "could not execute \"%s\": %m", 413 script); 414 } 415 _exit(EXIT_FAILURE); 416 default: 417 (void) wait(&status); 418 } 419 } 420 421 static void 422 list_interfaces(const char *ifnames) 423 { 424 char * names = strdup(ifnames); 425 char * name, *lasts; 426 static const char sep[] = " \t"; 427 struct interface_data * p; 428 429 for (name = strtok_r(names, sep, &lasts); 430 name != NULL; 431 name = strtok_r(NULL, sep, &lasts)) { 432 p = malloc(sizeof(*p)); 433 SLIST_INSERT_HEAD(&ifs, p, next); 434 p->last_carrier_status = -1; 435 p->ifname = strdup(name); 436 p->index = if_nametoindex(p->ifname); 437 if (!quiet) 438 syslog(LOG_INFO, "watching interface %s", p->ifname); 439 if (verbose) 440 printf("interface \"%s\" has index %d\n", 441 p->ifname, p->index); 442 } 443 free(names); 444 } 445 446 static void 447 check_carrier(const struct if_msghdr *ifm) 448 { 449 struct interface_data * p; 450 int carrier_status; 451 enum event ev; 452 453 SLIST_FOREACH(p, &ifs, next) 454 if (p->index == ifm->ifm_index) 455 break; 456 457 if (p == NULL) 458 return; 459 460 /* 461 * Treat it as an event worth handling if: 462 * - the carrier status changed, or 463 * - this is the first time we've been called, and 464 * inhibit_initial is not set 465 */ 466 carrier_status = ifm->ifm_data.ifi_link_state; 467 if (carrier_status != p->last_carrier_status) { 468 switch (carrier_status) { 469 case LINK_STATE_UP: 470 ev = CARRIER; 471 break; 472 case LINK_STATE_DOWN: 473 ev = NO_CARRIER; 474 break; 475 default: 476 if (verbose) 477 printf("unknown link status ignored\n"); 478 return; 479 } 480 invoke_script(p->ifname, ev, NULL, NULL); 481 p->last_carrier_status = carrier_status; 482 } 483 } 484 485 static void 486 check_announce(const struct if_announcemsghdr *ifan) 487 { 488 struct interface_data * p; 489 const char *ifname = ifan->ifan_name; 490 491 SLIST_FOREACH(p, &ifs, next) { 492 if (strcmp(p->ifname, ifname) != 0) 493 continue; 494 495 switch (ifan->ifan_what) { 496 case IFAN_ARRIVAL: 497 p->index = ifan->ifan_index; 498 invoke_script(p->ifname, ARRIVAL, NULL, NULL); 499 break; 500 case IFAN_DEPARTURE: 501 p->index = -1; 502 p->last_carrier_status = -1; 503 invoke_script(p->ifname, DEPARTURE, NULL, NULL); 504 break; 505 default: 506 if (verbose) 507 (void) printf("unknown announce: " 508 "what=%d\n", ifan->ifan_what); 509 break; 510 } 511 return; 512 } 513 } 514 515 static void 516 free_interfaces(void) 517 { 518 struct interface_data * p; 519 520 while (!SLIST_EMPTY(&ifs)) { 521 p = SLIST_FIRST(&ifs); 522 SLIST_REMOVE_HEAD(&ifs, next); 523 free(p->ifname); 524 free(p); 525 } 526 } 527 528 static struct interface_data * 529 find_interface(int idx) 530 { 531 struct interface_data * p; 532 533 SLIST_FOREACH(p, &ifs, next) 534 if (p->index == idx) 535 return p; 536 return NULL; 537 } 538 539 static void 540 run_initial_ups(void) 541 { 542 struct interface_data * ifd; 543 struct ifaddrs *res = NULL, *p; 544 struct sockaddr *ifa; 545 int s, aflag; 546 547 s = socket(AF_INET, SOCK_DGRAM, 0); 548 if (s < 0) 549 return; 550 551 if (getifaddrs(&res) != 0) 552 goto out; 553 554 for (p = res; p; p = p->ifa_next) { 555 SLIST_FOREACH(ifd, &ifs, next) { 556 if (strcmp(ifd->ifname, p->ifa_name) == 0) 557 break; 558 } 559 if (ifd == NULL) 560 continue; 561 562 ifa = p->ifa_addr; 563 if (ifa != NULL && ifa->sa_family == AF_LINK) 564 invoke_script(ifd->ifname, ARRIVAL, NULL, NULL); 565 566 if ((p->ifa_flags & IFF_UP) == 0) 567 continue; 568 if (ifa == NULL) 569 continue; 570 if (ifa->sa_family == AF_LINK) { 571 struct ifmediareq ifmr; 572 573 memset(&ifmr, 0, sizeof(ifmr)); 574 strncpy(ifmr.ifm_name, ifd->ifname, 575 sizeof(ifmr.ifm_name)); 576 if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 577 && (ifmr.ifm_status & IFM_AVALID) 578 && (ifmr.ifm_status & IFM_ACTIVE)) { 579 invoke_script(ifd->ifname, CARRIER, NULL, NULL); 580 ifd->last_carrier_status = 581 LINK_STATE_UP; 582 } 583 continue; 584 } 585 aflag = check_addrflags(ifa->sa_family, p->ifa_addrflags); 586 if (aflag != READY) 587 continue; 588 invoke_script(ifd->ifname, UP, ifa, p->ifa_dstaddr); 589 } 590 freeifaddrs(res); 591 out: 592 close(s); 593 } 594