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