1 /* $NetBSD: ifwatchd.c,v 1.14 2003/06/23 21:50:12 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 #ifdef SPPP_IF_SUPPORT 53 #include <net/if_sppp.h> 54 #endif 55 #include <net/route.h> 56 #include <netinet/in.h> 57 #include <arpa/inet.h> 58 59 #include <paths.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #include <netdb.h> 65 #include <err.h> 66 #include <ifaddrs.h> 67 #include <syslog.h> 68 69 enum event { ARRIVAL, DEPARTURE, UP, DOWN }; 70 /* local functions */ 71 static void usage(void); 72 static void dispatch(void*, size_t); 73 static void check_addrs(char *cp, int addrs, enum event ev); 74 static void invoke_script(struct sockaddr *sa, struct sockaddr *dst, enum event ev, int ifindex, const char *ifname_hint); 75 static void list_interfaces(const char *ifnames); 76 static void check_announce(struct if_announcemsghdr *ifan); 77 static void rescan_interfaces(void); 78 static void free_interfaces(void); 79 static int find_interface(int index); 80 static void run_initial_ups(void); 81 82 #ifdef SPPP_IF_SUPPORT 83 static int if_is_connected(const char * ifname); 84 #else 85 #define if_is_connected(X) 1 86 #endif 87 88 /* stolen from /sbin/route */ 89 #define ROUNDUP(a) \ 90 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 91 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 92 93 /* global variables */ 94 static int verbose = 0, quiet = 0; 95 static int inhibit_initial = 0; 96 static const char *arrival_script = NULL; 97 static const char *departure_script = NULL; 98 static const char *up_script = NULL; 99 static const char *down_script = NULL; 100 static char DummyTTY[] = _PATH_DEVNULL; 101 static char DummySpeed[] = "9600"; 102 static const char **scripts[] = { 103 &arrival_script, 104 &departure_script, 105 &up_script, 106 &down_script 107 }; 108 109 struct interface_data { 110 SLIST_ENTRY(interface_data) next; 111 int index; 112 char * ifname; 113 }; 114 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); 115 116 int 117 main(int argc, char **argv) 118 { 119 int c, s, n; 120 int errs = 0; 121 char msg[2048], *msgp; 122 123 openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON); 124 while ((c = getopt(argc, argv, "qvhiu:d:A:D:")) != -1) 125 switch (c) { 126 case 'h': 127 usage(); 128 return 0; 129 case 'i': 130 inhibit_initial = 1; 131 break; 132 case 'v': 133 verbose++; 134 break; 135 case 'q': 136 quiet = 1; 137 break; 138 139 case 'u': 140 up_script = optarg; 141 break; 142 143 case 'd': 144 down_script = optarg; 145 break; 146 147 case 'A': 148 arrival_script = optarg; 149 break; 150 151 case 'D': 152 departure_script = optarg; 153 break; 154 155 default: 156 errs++; 157 break; 158 } 159 160 if (errs) 161 usage(); 162 163 argv += optind; 164 argc -= optind; 165 166 if (argc <= 0) 167 usage(); 168 169 if (verbose) { 170 printf("up_script: %s\ndown_script: %s\n", 171 up_script, down_script); 172 printf("arrival_script: %s\ndeparture_script: %s\n", 173 arrival_script, departure_script); 174 printf("verbosity = %d\n", verbose); 175 } 176 177 while (argc > 0) { 178 list_interfaces(argv[0]); 179 argv++; 180 argc--; 181 } 182 183 if (!verbose) 184 daemon(0,0); 185 186 s = socket(PF_ROUTE, SOCK_RAW, 0); 187 if (s < 0) { 188 syslog(LOG_ERR, "error opening routing socket: %m"); 189 perror("open routing socket"); 190 exit(EXIT_FAILURE); 191 } 192 193 if (!inhibit_initial) 194 run_initial_ups(); 195 196 for (;;) { 197 n = read(s, msg, sizeof msg); 198 msgp = msg; 199 for (msgp = msg; n > 0; n -= ((struct rt_msghdr*)msgp)->rtm_msglen, msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) { 200 dispatch(msgp, n); 201 202 } 203 } 204 205 close(s); 206 free_interfaces(); 207 closelog(); 208 209 return EXIT_SUCCESS; 210 } 211 212 static void 213 usage() 214 { 215 fprintf(stderr, 216 "usage:\n" 217 "\tifwatchd [-hiv] [-A arrival-script] [-D departure-script]\n" 218 "\t\t [-d down-script] [-u up-script] ifname(s)\n" 219 "\twhere:\n" 220 "\t -A <cmd> specify command to run on interface arrival event\n" 221 "\t -D <cmd> specify command to run on interface departure event\n" 222 "\t -d <cmd> specify command to run on interface down event\n" 223 "\t -h show this help message\n" 224 "\t -i no (!) initial run of the up script if the interface\n" 225 "\t is already up on ifwatchd startup\n" 226 "\t -u <cmd> specify command to run on interface up event\n" 227 "\t -v verbose/debug output, don't run in background\n" 228 "\t -q quiet mode, don't syslog informational messages\n"); 229 exit(EXIT_FAILURE); 230 } 231 232 static void 233 dispatch(void *msg, size_t len) 234 { 235 struct rt_msghdr *hd = msg; 236 struct ifa_msghdr *ifam; 237 enum event ev; 238 239 switch (hd->rtm_type) { 240 case RTM_NEWADDR: 241 ev = UP; 242 goto work; 243 case RTM_DELADDR: 244 ev = DOWN; 245 goto work; 246 case RTM_IFANNOUNCE: 247 rescan_interfaces(); 248 check_announce((struct if_announcemsghdr *)msg); 249 return; 250 } 251 if (verbose) 252 printf("unknown message ignored\n"); 253 return; 254 255 work: 256 ifam = (struct ifa_msghdr *)msg; 257 check_addrs((char *)(ifam + 1), ifam->ifam_addrs, ev); 258 } 259 260 static void 261 check_addrs(cp, addrs, ev) 262 char *cp; 263 int addrs; 264 enum event ev; 265 { 266 struct sockaddr *sa, *ifa = NULL, *brd = NULL; 267 int ifndx = 0, i; 268 269 if (addrs == 0) 270 return; 271 for (i = 1; i; i <<= 1) { 272 if (i & addrs) { 273 sa = (struct sockaddr *)cp; 274 if (i == RTA_IFP) { 275 struct sockaddr_dl * li = (struct sockaddr_dl*)sa; 276 ifndx = li->sdl_index; 277 if (!find_interface(ifndx)) { 278 if (verbose) 279 printf("ignoring change on interface #%d\n", ifndx); 280 return; 281 } 282 } else if (i == RTA_IFA) { 283 ifa = sa; 284 } else if (i == RTA_BRD) { 285 brd = sa; 286 } 287 ADVANCE(cp, sa); 288 } 289 } 290 if (ifa != NULL) 291 invoke_script(ifa, brd, ev, ifndx, NULL); 292 } 293 294 static void 295 invoke_script(sa, dest, ev, ifindex, ifname_hint) 296 struct sockaddr *sa, *dest; 297 enum event ev; 298 int ifindex; 299 const char *ifname_hint; 300 { 301 char addr[NI_MAXHOST], daddr[NI_MAXHOST], ifname_buf[IFNAMSIZ]; 302 const char *ifname; 303 const char *script; 304 int status; 305 306 if (sa != NULL && sa->sa_len == 0) { 307 fprintf(stderr, "illegal socket address (sa_len == 0)\n"); 308 return; 309 } 310 if (sa != NULL && sa->sa_family == AF_INET6) { 311 struct sockaddr_in6 sin6; 312 313 (void) memcpy(&sin6, (struct sockaddr_in6 *)sa, sizeof (sin6)); 314 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) 315 return; 316 } 317 318 addr[0] = daddr[0] = 0; 319 ifname = if_indextoname(ifindex, ifname_buf); 320 ifname = ifname ? ifname : ifname_hint; 321 if (ifname == NULL) 322 return; 323 324 if (sa != NULL) { 325 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, 326 NI_NUMERICHOST)) { 327 if (verbose) 328 printf("getnameinfo failed\n"); 329 return; /* this address can not be handled */ 330 } 331 } 332 if (dest != NULL) { 333 if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr, 334 NULL, 0, NI_NUMERICHOST)) { 335 if (verbose) 336 printf("getnameinfo failed\n"); 337 return; /* this address can not be handled */ 338 } 339 } 340 341 script = *scripts[ev]; 342 if (script == NULL) return; 343 344 if (verbose) 345 (void) printf("calling: %s %s %s %s %s %s\n", 346 script, ifname, DummyTTY, DummySpeed, addr, daddr); 347 if (!quiet) 348 syslog(LOG_INFO, "calling: %s %s %s %s %s %s\n", 349 script, ifname, DummyTTY, DummySpeed, addr, daddr); 350 351 switch (vfork()) { 352 case -1: 353 fprintf(stderr, "cannot fork\n"); 354 break; 355 case 0: 356 if (execl(script, script, ifname, DummyTTY, DummySpeed, 357 addr, daddr, NULL) == -1) { 358 syslog(LOG_ERR, "could not execute \"%s\": %m", 359 script); 360 perror(script); 361 } 362 _exit(EXIT_FAILURE); 363 default: 364 (void) wait(&status); 365 } 366 } 367 368 static void list_interfaces(const char *ifnames) 369 { 370 char * names = strdup(ifnames); 371 char * name, *lasts; 372 static const char sep[] = " \t"; 373 struct interface_data * p; 374 375 for (name = strtok_r(names, sep, &lasts); 376 name != NULL; 377 name = strtok_r(NULL, sep, &lasts)) { 378 p = malloc(sizeof(*p)); 379 SLIST_INSERT_HEAD(&ifs, p, next); 380 p->ifname = strdup(name); 381 p->index = if_nametoindex(p->ifname); 382 if (!quiet) 383 syslog(LOG_INFO, "watching interface %s", p->ifname); 384 if (verbose) 385 printf("interface \"%s\" has index %d\n", 386 p->ifname, p->index); 387 } 388 free(names); 389 } 390 391 static void 392 check_announce(struct if_announcemsghdr *ifan) 393 { 394 struct interface_data * p; 395 const char *ifname = ifan->ifan_name; 396 397 SLIST_FOREACH(p, &ifs, next) { 398 if (strcmp(p->ifname, ifname) == 0) { 399 switch (ifan->ifan_what) { 400 case IFAN_ARRIVAL: 401 invoke_script(NULL, NULL, ARRIVAL, p->index, 402 NULL); 403 break; 404 case IFAN_DEPARTURE: 405 invoke_script(NULL, NULL, DEPARTURE, p->index, 406 p->ifname); 407 break; 408 default: 409 if (verbose) 410 (void) printf("unknown announce: " 411 "what=%d\n", ifan->ifan_what); 412 break; 413 } 414 return; 415 } 416 } 417 } 418 419 static void rescan_interfaces() 420 { 421 struct interface_data * p; 422 423 SLIST_FOREACH(p, &ifs, next) { 424 p->index = if_nametoindex(p->ifname); 425 if (verbose) 426 printf("interface \"%s\" has index %d\n", p->ifname, 427 p->index); 428 } 429 } 430 431 static void free_interfaces() 432 { 433 struct interface_data * p; 434 435 while (!SLIST_EMPTY(&ifs)) { 436 p = SLIST_FIRST(&ifs); 437 SLIST_REMOVE_HEAD(&ifs, next); 438 free(p->ifname); 439 free(p); 440 } 441 } 442 443 static int find_interface(index) 444 int index; 445 { 446 struct interface_data * p; 447 448 SLIST_FOREACH(p, &ifs, next) 449 if (p->index == index) 450 return 1; 451 return 0; 452 } 453 454 static void run_initial_ups() 455 { 456 struct interface_data * ifd; 457 struct ifaddrs *res = NULL, *p; 458 459 if (getifaddrs(&res) == 0) { 460 for (p = res; p; p = p->ifa_next) { 461 SLIST_FOREACH(ifd, &ifs, next) { 462 if (strcmp(ifd->ifname, p->ifa_name) == 0) 463 break; 464 } 465 if (ifd == NULL) 466 continue; 467 468 if (p->ifa_addr && p->ifa_addr->sa_family == AF_LINK) 469 invoke_script(NULL, NULL, ARRIVAL, ifd->index, 470 NULL); 471 472 if ((p->ifa_flags & IFF_UP) == 0) 473 continue; 474 if (p->ifa_addr == NULL) 475 continue; 476 if (p->ifa_addr->sa_family == AF_LINK) 477 continue; 478 if (if_is_connected(ifd->ifname)) 479 invoke_script(p->ifa_addr, p->ifa_dstaddr, UP, 480 ifd->index, ifd->ifname); 481 } 482 freeifaddrs(res); 483 } 484 } 485 486 #ifdef SPPP_IF_SUPPORT 487 /* 488 * Special case support for in-kernel PPP interfaces. 489 * If these are IFF_UP, but have not yet connected or completed authentication 490 * we don't want to call the up script in the initial interface scan (there 491 * will be an UP event generated later, when IPCP completes, anyway). 492 * 493 * If this is no if_spppsubr.c based interface, this ioctl just fails and we 494 * treat is as connected. 495 */ 496 static int 497 if_is_connected(const char * ifname) 498 { 499 int s, err; 500 struct spppstatus status; 501 502 memset(&status, 0, sizeof status); 503 strncpy(status.ifname, ifname, sizeof status.ifname); 504 s = socket(AF_INET, SOCK_DGRAM, 0); 505 if (s < 0) 506 return 1; /* no idea how to handle this... */ 507 err = ioctl(s, SPPPGETSTATUS, &status); 508 if (err != 0) 509 /* not if_spppsubr.c based - call it connected */ 510 status.phase = SPPP_PHASE_NETWORK; 511 close(s); 512 return status.phase == SPPP_PHASE_NETWORK; 513 } 514 #endif 515