1 /* $NetBSD: ifwatchd.c,v 1.23 2008/05/24 17:45:14 joerg 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 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 /* stolen from /sbin/route */ 88 #define ROUNDUP(a) \ 89 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 90 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 91 92 /* global variables */ 93 static int verbose = 0, quiet = 0; 94 static int inhibit_initial = 0; 95 static const char *arrival_script = NULL; 96 static const char *departure_script = NULL; 97 static const char *up_script = NULL; 98 static const char *down_script = NULL; 99 static const char *carrier_script = NULL; 100 static const char *no_carrier_script = NULL; 101 static const char DummyTTY[] = _PATH_DEVNULL; 102 static const char DummySpeed[] = "9600"; 103 static const char **scripts[] = { 104 &arrival_script, 105 &departure_script, 106 &up_script, 107 &down_script, 108 &carrier_script, 109 &no_carrier_script 110 }; 111 112 struct interface_data { 113 SLIST_ENTRY(interface_data) next; 114 int index; 115 int last_carrier_status; 116 char * ifname; 117 }; 118 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); 119 120 int 121 main(int argc, char **argv) 122 { 123 int c, s, n; 124 int errs = 0; 125 char msg[2048], *msgp; 126 127 openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON); 128 while ((c = getopt(argc, argv, "qvhic:n:u:d:A:D:")) != -1) { 129 switch (c) { 130 case 'h': 131 usage(); 132 return 0; 133 134 case 'i': 135 inhibit_initial = 1; 136 break; 137 138 case 'v': 139 verbose++; 140 break; 141 142 case 'q': 143 quiet = 1; 144 break; 145 146 case 'c': 147 carrier_script = optarg; 148 break; 149 150 case 'n': 151 no_carrier_script = optarg; 152 break; 153 154 case 'u': 155 up_script = optarg; 156 break; 157 158 case 'd': 159 down_script = optarg; 160 break; 161 162 case 'A': 163 arrival_script = optarg; 164 break; 165 166 case 'D': 167 departure_script = optarg; 168 break; 169 170 default: 171 errs++; 172 break; 173 } 174 } 175 176 if (errs) 177 usage(); 178 179 argv += optind; 180 argc -= optind; 181 182 if (argc <= 0) 183 usage(); 184 185 if (verbose) { 186 printf("up_script: %s\ndown_script: %s\n", 187 up_script, down_script); 188 printf("arrival_script: %s\ndeparture_script: %s\n", 189 arrival_script, departure_script); 190 printf("carrier_script: %s\nno_carrier_script: %s\n", 191 carrier_script, no_carrier_script); 192 printf("verbosity = %d\n", verbose); 193 } 194 195 while (argc > 0) { 196 list_interfaces(argv[0]); 197 argv++; 198 argc--; 199 } 200 201 if (!verbose) 202 daemon(0, 0); 203 204 s = socket(PF_ROUTE, SOCK_RAW, 0); 205 if (s < 0) { 206 syslog(LOG_ERR, "error opening routing socket: %m"); 207 perror("open routing socket"); 208 exit(EXIT_FAILURE); 209 } 210 211 if (!inhibit_initial) 212 run_initial_ups(); 213 214 for (;;) { 215 n = read(s, msg, sizeof msg); 216 msgp = msg; 217 for (msgp = msg; n > 0; 218 n -= ((struct rt_msghdr*)msgp)->rtm_msglen, 219 msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) 220 dispatch(msgp, n); 221 } 222 223 close(s); 224 free_interfaces(); 225 closelog(); 226 227 return EXIT_SUCCESS; 228 } 229 230 static void 231 usage(void) 232 { 233 fprintf(stderr, 234 "usage:\n" 235 "\tifwatchd [-hiqv] [-A arrival-script] [-D departure-script]\n" 236 "\t\t [-d down-script] [-u up-script]\n" 237 "\t\t [-c carrier-script] [-n no-carrier-script] ifname(s)\n" 238 "\twhere:\n" 239 "\t -A <cmd> specify command to run on interface arrival event\n" 240 "\t -c <cmd> specify command to run on interface carrier-detect event\n" 241 "\t -D <cmd> specify command to run on interface departure event\n" 242 "\t -d <cmd> specify command to run on interface down event\n" 243 "\t -n <cmd> specify command to run on interface no-carrier-detect event\n" 244 "\t -h show this help message\n" 245 "\t -i no (!) initial run of the up script if the interface\n" 246 "\t is already up on ifwatchd startup\n" 247 "\t -q quiet mode, don't syslog informational messages\n" 248 "\t -u <cmd> specify command to run on interface up event\n" 249 "\t -v verbose/debug output, don't run in background\n"); 250 exit(EXIT_FAILURE); 251 } 252 253 static void 254 dispatch(void *msg, size_t len) 255 { 256 struct rt_msghdr *hd = msg; 257 struct if_msghdr *ifmp; 258 struct ifa_msghdr *ifam; 259 enum event ev; 260 261 switch (hd->rtm_type) { 262 case RTM_NEWADDR: 263 ev = UP; 264 goto work; 265 case RTM_DELADDR: 266 ev = DOWN; 267 goto work; 268 case RTM_IFANNOUNCE: 269 rescan_interfaces(); 270 check_announce((struct if_announcemsghdr *)msg); 271 return; 272 case RTM_IFINFO: 273 ifmp = (struct if_msghdr*)msg; 274 check_carrier(ifmp->ifm_index, ifmp->ifm_data.ifi_link_state); 275 return; 276 case RTM_ADD: 277 case RTM_DELETE: 278 case RTM_CHANGE: 279 case RTM_LOSING: 280 case RTM_REDIRECT: 281 case RTM_MISS: 282 case RTM_IEEE80211: 283 return; 284 } 285 if (verbose) 286 printf("unknown message ignored (%d)\n", hd->rtm_type); 287 return; 288 289 work: 290 ifam = (struct ifa_msghdr *)msg; 291 check_addrs((char *)(ifam + 1), ifam->ifam_addrs, ev); 292 } 293 294 static void 295 check_addrs(char *cp, int addrs, enum event ev) 296 { 297 struct sockaddr *sa, *ifa = NULL, *brd = NULL; 298 char ifname_buf[IFNAMSIZ]; 299 const char *ifname; 300 int ifndx = 0, i; 301 302 if (addrs == 0) 303 return; 304 for (i = 1; i; i <<= 1) { 305 if ((i & addrs) == 0) 306 continue; 307 sa = (struct sockaddr *)cp; 308 if (i == RTA_IFP) { 309 struct sockaddr_dl * li = (struct sockaddr_dl*)sa; 310 ifndx = li->sdl_index; 311 if (!find_interface(ifndx)) { 312 if (verbose) 313 printf("ignoring change on interface #%d\n", ifndx); 314 return; 315 } 316 } else if (i == RTA_IFA) 317 ifa = sa; 318 else if (i == RTA_BRD) 319 brd = sa; 320 ADVANCE(cp, sa); 321 } 322 if (ifa != NULL) { 323 ifname = if_indextoname(ifndx, ifname_buf); 324 if (ifname == NULL || ev < UP) 325 invoke_script(ifa, brd, ev, ifndx, ifname); 326 else if (ev == UP) { 327 if (if_is_connected(ifname)) 328 invoke_script(ifa, brd, ev, ifndx, ifname); 329 } else if (ev == DOWN) { 330 if (if_is_not_connected(ifname)) 331 invoke_script(ifa, brd, ev, ifndx, ifname); 332 } 333 } 334 } 335 336 static void 337 invoke_script(struct sockaddr *sa, struct sockaddr *dest, enum event ev, 338 int ifindex, const char *ifname_hint) 339 { 340 char addr[NI_MAXHOST], daddr[NI_MAXHOST], ifname_buf[IFNAMSIZ]; 341 const char *ifname; 342 const char *script; 343 int status; 344 345 if (sa != NULL && sa->sa_len == 0) { 346 fprintf(stderr, "illegal socket address (sa_len == 0)\n"); 347 return; 348 } 349 if (sa != NULL && sa->sa_family == AF_INET6) { 350 struct sockaddr_in6 sin6; 351 352 (void) memcpy(&sin6, (struct sockaddr_in6 *)sa, sizeof (sin6)); 353 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) 354 return; 355 } 356 357 addr[0] = daddr[0] = 0; 358 ifname = if_indextoname(ifindex, ifname_buf); 359 ifname = ifname ? ifname : ifname_hint; 360 if (ifname == NULL) 361 return; 362 363 if (sa != NULL) { 364 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, 365 NI_NUMERICHOST)) { 366 if (verbose) 367 printf("getnameinfo failed\n"); 368 return; /* this address can not be handled */ 369 } 370 } 371 if (dest != NULL) { 372 if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr, 373 NULL, 0, NI_NUMERICHOST)) { 374 if (verbose) 375 printf("getnameinfo failed\n"); 376 return; /* this address can not be handled */ 377 } 378 } 379 380 script = *scripts[ev]; 381 if (script == NULL) return; 382 383 if (verbose) 384 (void) printf("calling: %s %s %s %s %s %s\n", 385 script, ifname, DummyTTY, DummySpeed, addr, daddr); 386 if (!quiet) 387 syslog(LOG_INFO, "calling: %s %s %s %s %s %s\n", 388 script, ifname, DummyTTY, DummySpeed, addr, daddr); 389 390 switch (vfork()) { 391 case -1: 392 fprintf(stderr, "cannot fork\n"); 393 break; 394 case 0: 395 if (execl(script, script, ifname, DummyTTY, DummySpeed, 396 addr, daddr, NULL) == -1) { 397 syslog(LOG_ERR, "could not execute \"%s\": %m", 398 script); 399 perror(script); 400 } 401 _exit(EXIT_FAILURE); 402 default: 403 (void) wait(&status); 404 } 405 } 406 407 static void 408 list_interfaces(const char *ifnames) 409 { 410 char * names = strdup(ifnames); 411 char * name, *lasts; 412 static const char sep[] = " \t"; 413 struct interface_data * p; 414 415 for (name = strtok_r(names, sep, &lasts); 416 name != NULL; 417 name = strtok_r(NULL, sep, &lasts)) { 418 p = malloc(sizeof(*p)); 419 SLIST_INSERT_HEAD(&ifs, p, next); 420 p->last_carrier_status = -1; 421 p->ifname = strdup(name); 422 p->index = if_nametoindex(p->ifname); 423 if (!quiet) 424 syslog(LOG_INFO, "watching interface %s", p->ifname); 425 if (verbose) 426 printf("interface \"%s\" has index %d\n", 427 p->ifname, p->index); 428 } 429 free(names); 430 } 431 432 static void 433 check_carrier(int if_index, int carrier_status) 434 { 435 struct interface_data * p; 436 enum event ev; 437 438 SLIST_FOREACH(p, &ifs, next) 439 if (p->index == if_index) 440 break; 441 442 if (p == NULL) 443 return; 444 445 /* 446 * Treat it as an event worth handling if: 447 * - the carrier status changed, or 448 * - this is the first time we've been called, and 449 * inhibit_initial is not set 450 */ 451 452 if ((carrier_status != p->last_carrier_status) || 453 ((p->last_carrier_status == -1) && !inhibit_initial)) { 454 switch (carrier_status) { 455 case LINK_STATE_UP: 456 ev = CARRIER; 457 break; 458 case LINK_STATE_DOWN: 459 ev = NO_CARRIER; 460 break; 461 default: 462 if (verbose) 463 printf("unknown link status ignored\n"); 464 return; 465 } 466 invoke_script(NULL, NULL, ev, if_index, p->ifname); 467 p->last_carrier_status = carrier_status; 468 } 469 } 470 471 static void 472 check_announce(struct if_announcemsghdr *ifan) 473 { 474 struct interface_data * p; 475 const char *ifname = ifan->ifan_name; 476 477 SLIST_FOREACH(p, &ifs, next) { 478 if (strcmp(p->ifname, ifname) != 0) 479 continue; 480 481 switch (ifan->ifan_what) { 482 case IFAN_ARRIVAL: 483 invoke_script(NULL, NULL, ARRIVAL, p->index, 484 NULL); 485 break; 486 case IFAN_DEPARTURE: 487 invoke_script(NULL, NULL, DEPARTURE, p->index, 488 p->ifname); 489 break; 490 default: 491 if (verbose) 492 (void) printf("unknown announce: " 493 "what=%d\n", ifan->ifan_what); 494 break; 495 } 496 return; 497 } 498 } 499 500 static void 501 rescan_interfaces(void) 502 { 503 struct interface_data * p; 504 505 SLIST_FOREACH(p, &ifs, next) { 506 p->index = if_nametoindex(p->ifname); 507 if (verbose) 508 printf("interface \"%s\" has index %d\n", p->ifname, 509 p->index); 510 } 511 } 512 513 static void 514 free_interfaces(void) 515 { 516 struct interface_data * p; 517 518 while (!SLIST_EMPTY(&ifs)) { 519 p = SLIST_FIRST(&ifs); 520 SLIST_REMOVE_HEAD(&ifs, next); 521 free(p->ifname); 522 free(p); 523 } 524 } 525 526 static int 527 find_interface(int index) 528 { 529 struct interface_data * p; 530 531 SLIST_FOREACH(p, &ifs, next) 532 if (p->index == index) 533 return 1; 534 return 0; 535 } 536 537 static void 538 run_initial_ups(void) 539 { 540 struct interface_data * ifd; 541 struct ifaddrs *res = NULL, *p; 542 int s; 543 544 s = socket(AF_INET, SOCK_DGRAM, 0); 545 if (s < 0) 546 return; 547 548 if (getifaddrs(&res) != 0) 549 goto out; 550 551 for (p = res; p; p = p->ifa_next) { 552 SLIST_FOREACH(ifd, &ifs, next) { 553 if (strcmp(ifd->ifname, p->ifa_name) == 0) 554 break; 555 } 556 if (ifd == NULL) 557 continue; 558 559 if (p->ifa_addr && p->ifa_addr->sa_family == AF_LINK) 560 invoke_script(NULL, NULL, ARRIVAL, ifd->index, 561 NULL); 562 563 if ((p->ifa_flags & IFF_UP) == 0) 564 continue; 565 if (p->ifa_addr == NULL) 566 continue; 567 if (p->ifa_addr->sa_family == AF_LINK) { 568 struct ifmediareq ifmr; 569 570 memset(&ifmr, 0, sizeof(ifmr)); 571 strncpy(ifmr.ifm_name, ifd->ifname, 572 sizeof(ifmr.ifm_name)); 573 if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 574 && (ifmr.ifm_status & IFM_AVALID) 575 && (ifmr.ifm_status & IFM_ACTIVE)) { 576 invoke_script(NULL, NULL, CARRIER, 577 ifd->index, ifd->ifname); 578 ifd->last_carrier_status = 579 LINK_STATE_UP; 580 } 581 continue; 582 } 583 if (if_is_connected(ifd->ifname)) 584 invoke_script(p->ifa_addr, p->ifa_dstaddr, UP, 585 ifd->index, ifd->ifname); 586 } 587 freeifaddrs(res); 588 out: 589 close(s); 590 } 591 592 #ifdef SPPP_IF_SUPPORT 593 /* 594 * Special case support for in-kernel PPP interfaces. 595 * If these are IFF_UP, but have not yet connected or completed authentication 596 * we don't want to call the up script in the initial interface scan (there 597 * will be an UP event generated later, when IPCP completes, anyway). 598 * 599 * If this is no if_spppsubr.c based interface, this ioctl just fails and we 600 * treat is as connected. 601 */ 602 static int 603 check_is_connected(const char *ifname, int def_retval) 604 { 605 int s, err; 606 struct spppstatus oldstatus; 607 struct spppstatusncp status; 608 609 memset(&status, 0, sizeof status); 610 strncpy(status.ifname, ifname, sizeof status.ifname); 611 memset(&oldstatus, 0, sizeof oldstatus); 612 strncpy(oldstatus.ifname, ifname, sizeof oldstatus.ifname); 613 614 s = socket(AF_INET, SOCK_DGRAM, 0); 615 if (s < 0) 616 return 1; /* no idea how to handle this... */ 617 err = ioctl(s, SPPPGETSTATUSNCP, &status); 618 if (err != 0) { 619 err = ioctl(s, SPPPGETSTATUS, &oldstatus); 620 if (err != 0) { 621 /* not if_spppsubr.c based - return default */ 622 close(s); 623 return def_retval; 624 } else { 625 /* can't query NCPs, so use default */ 626 status.phase = oldstatus.phase; 627 status.ncpup = def_retval; 628 } 629 } 630 close(s); 631 632 return status.phase == SPPP_PHASE_NETWORK && status.ncpup > 0; 633 } 634 #endif 635