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