1 /* $NetBSD: ifwatchd.c,v 1.9 2002/04/15 21:08:41 tron Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 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 68 /* local functions */ 69 static void usage(void); 70 static void dispatch(void*, size_t); 71 static void check_addrs(char *cp, int addrs, int is_up); 72 static void invoke_script(struct sockaddr *sa, struct sockaddr *dst, int is_up, int ifindex); 73 static void list_interfaces(const char *ifnames); 74 static void rescan_interfaces(void); 75 static void free_interfaces(void); 76 static int find_interface(int index); 77 static void run_initial_ups(void); 78 79 #ifdef SPPP_IF_SUPPORT 80 static int if_is_connected(const char * ifname); 81 #else 82 #define if_is_connected(X) 1 83 #endif 84 85 /* stolen from /sbin/route */ 86 #define ROUNDUP(a) \ 87 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 88 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 89 90 /* global variables */ 91 static int verbose = 0; 92 static int inhibit_initial = 0; 93 static const char *up_script = NULL; 94 static const char *down_script = NULL; 95 static char DummyTTY[] = _PATH_DEVNULL; 96 static char DummySpeed[] = "9600"; 97 98 struct interface_data { 99 SLIST_ENTRY(interface_data) next; 100 int index; 101 char * ifname; 102 }; 103 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); 104 105 int 106 main(int argc, char **argv) 107 { 108 int c, s, n; 109 int errs = 0; 110 char msg[2048], *msgp; 111 112 while ((c = getopt(argc, argv, "vhiu:d:")) != -1) 113 switch (c) { 114 case 'h': 115 usage(); 116 return 0; 117 case 'i': 118 inhibit_initial = 1; 119 break; 120 case 'v': 121 verbose++; 122 break; 123 124 case 'u': 125 up_script = optarg; 126 break; 127 128 case 'd': 129 down_script = optarg; 130 break; 131 132 default: 133 errs++; 134 break; 135 } 136 137 if (errs) 138 usage(); 139 140 argv += optind; 141 argc -= optind; 142 143 if (argc <= 0) 144 usage(); 145 146 if (verbose) { 147 printf("up_script: %s\ndown_script: %s\n", 148 up_script, down_script); 149 printf("verbosity = %d\n", verbose); 150 } 151 152 while (argc > 0) { 153 list_interfaces(argv[0]); 154 argv++; argc--; 155 } 156 157 if (!verbose) 158 daemon(0,0); 159 160 if (!inhibit_initial) 161 run_initial_ups(); 162 163 s = socket(PF_ROUTE, SOCK_RAW, 0); 164 if (s < 0) { 165 perror("open routing socket"); 166 exit(EXIT_FAILURE); 167 } 168 169 for (;;) { 170 n = read(s, msg, sizeof msg); 171 msgp = msg; 172 for (msgp = msg; n > 0; n -= ((struct rt_msghdr*)msgp)->rtm_msglen, msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) { 173 dispatch(msgp, n); 174 175 } 176 } 177 178 close(s); 179 free_interfaces(); 180 181 return EXIT_SUCCESS; 182 } 183 184 static void 185 usage() 186 { 187 fprintf(stderr, 188 "usage:\n" 189 "\tifwatchd [-h] [-v] [-u up-script] [-d down-script] ifname(s)\n" 190 "\twhere:\n" 191 "\t -h show this help message\n" 192 "\t -v verbose/debug output, don't run in background\n" 193 "\t -i no (!) initial run of the up script if the interface\n" 194 "\t is already up on ifwatchd startup\n" 195 "\t -u <cmd> specify command to run on interface up event\n" 196 "\t -d <cmd> specify command to run on interface down event\n"); 197 exit(EXIT_FAILURE); 198 } 199 200 static void 201 dispatch(void *msg, size_t len) 202 { 203 struct rt_msghdr *hd = msg; 204 struct ifa_msghdr *ifam; 205 int is_up; 206 207 is_up = 0; 208 switch (hd->rtm_type) { 209 case RTM_NEWADDR: 210 is_up = 1; 211 goto work; 212 case RTM_DELADDR: 213 is_up = 0; 214 goto work; 215 case RTM_IFANNOUNCE: 216 rescan_interfaces(); 217 break; 218 } 219 if (verbose) 220 printf("unknown message ignored\n"); 221 return; 222 223 work: 224 ifam = (struct ifa_msghdr *)msg; 225 check_addrs((char *)(ifam + 1), ifam->ifam_addrs, is_up); 226 } 227 228 static void 229 check_addrs(cp, addrs, is_up) 230 char *cp; 231 int addrs, is_up; 232 { 233 struct sockaddr *sa, *ifa = NULL, *brd = NULL; 234 int ifndx = 0, i; 235 236 if (addrs == 0) 237 return; 238 for (i = 1; i; i <<= 1) { 239 if (i & addrs) { 240 sa = (struct sockaddr *)cp; 241 if (i == RTA_IFP) { 242 struct sockaddr_dl * li = (struct sockaddr_dl*)sa; 243 ifndx = li->sdl_index; 244 if (!find_interface(ifndx)) { 245 if (verbose) 246 printf("ignoring change on interface #%d\n", ifndx); 247 return; 248 } 249 } else if (i == RTA_IFA) { 250 ifa = sa; 251 } else if (i == RTA_BRD) { 252 brd = sa; 253 } 254 ADVANCE(cp, sa); 255 } 256 } 257 if (ifa != NULL) 258 invoke_script(ifa, brd, is_up, ifndx); 259 } 260 261 static void 262 invoke_script(sa, dest, is_up, ifindex) 263 struct sockaddr *sa, *dest; 264 int is_up, ifindex; 265 { 266 char addr[NI_MAXHOST], daddr[NI_MAXHOST], ifname_buf[IFNAMSIZ], 267 *ifname; 268 const char *script; 269 int status; 270 271 if (sa->sa_family == AF_INET6) { 272 struct sockaddr_in6 sin6; 273 274 (void) memcpy(&sin6, (struct sockaddr_in6 *)sa, sizeof (sin6)); 275 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) 276 return; 277 } 278 279 daddr[0] = 0; 280 ifname = if_indextoname(ifindex, ifname_buf); 281 if (sa->sa_len == 0) { 282 fprintf(stderr, "illegal socket address (sa_len == 0)\n"); 283 return; 284 } 285 286 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, NI_NUMERICHOST)) { 287 if (verbose) 288 printf("getnameinfo failed\n"); 289 return; /* this address can not be handled */ 290 } 291 if (dest != NULL) { 292 if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr, NULL, 0, NI_NUMERICHOST)) { 293 if (verbose) 294 printf("getnameinfo failed\n"); 295 return; /* this address can not be handled */ 296 } 297 } 298 299 script = is_up? up_script : down_script; 300 if (script == NULL) return; 301 302 if (verbose) 303 (void) printf("calling: %s %s %s %s %s %s\n", 304 script, ifname, DummyTTY, DummySpeed, addr, daddr); 305 306 switch (vfork()) { 307 case -1: 308 fprintf(stderr, "cannot fork\n"); 309 break; 310 case 0: 311 (void) execl(script, script, ifname, DummyTTY, DummySpeed, 312 addr, daddr, NULL); 313 _exit(EXIT_FAILURE); 314 default: 315 (void) wait(&status); 316 } 317 } 318 319 static void list_interfaces(const char *ifnames) 320 { 321 char * names = strdup(ifnames); 322 char * name, *lasts; 323 static const char sep[] = " \t"; 324 struct interface_data * p; 325 326 for (name = strtok_r(names, sep, &lasts); name != NULL; name = strtok_r(NULL, sep, &lasts)) { 327 p = malloc(sizeof(*p)); 328 SLIST_INSERT_HEAD(&ifs, p, next); 329 p->ifname = strdup(name); 330 p->index = if_nametoindex(p->ifname); 331 if (verbose) 332 printf("interface \"%s\" has index %d\n", p->ifname, p->index); 333 } 334 free(names); 335 } 336 337 static void rescan_interfaces() 338 { 339 struct interface_data * p; 340 341 SLIST_FOREACH(p, &ifs, next) { 342 p->index = if_nametoindex(p->ifname); 343 if (verbose) 344 printf("interface \"%s\" has index %d\n", p->ifname, p->index); 345 } 346 } 347 348 static void free_interfaces() 349 { 350 struct interface_data * p; 351 352 while (!SLIST_EMPTY(&ifs)) { 353 p = SLIST_FIRST(&ifs); 354 SLIST_REMOVE_HEAD(&ifs, next); 355 free(p->ifname); 356 free(p); 357 } 358 } 359 360 static int find_interface(index) 361 int index; 362 { 363 struct interface_data * p; 364 365 SLIST_FOREACH(p, &ifs, next) 366 if (p->index == index) 367 return 1; 368 return 0; 369 } 370 371 static void run_initial_ups() 372 { 373 struct interface_data * ifd; 374 struct ifaddrs *res = NULL, *p; 375 376 if (getifaddrs(&res) == 0) { 377 for (p = res; p; p = p->ifa_next) { 378 if ((p->ifa_flags & IFF_UP) == 0) 379 continue; 380 if (p->ifa_addr == NULL) 381 continue; 382 if (p->ifa_addr->sa_family == AF_LINK) 383 continue; 384 SLIST_FOREACH(ifd, &ifs, next) { 385 if (strcmp(ifd->ifname, p->ifa_name) == 0) { 386 if (if_is_connected(ifd->ifname)) 387 invoke_script(p->ifa_addr, p->ifa_dstaddr, 1, ifd->index); 388 break; 389 } 390 } 391 } 392 freeifaddrs(res); 393 } 394 } 395 396 #ifdef SPPP_IF_SUPPORT 397 /* 398 * Special case support for in-kernel PPP interfaces. 399 * If these are IFF_UP, but have not yet connected or completed authentication 400 * we don't want to call the up script in the initial interface scan (there 401 * will be an UP event generated later, when IPCP completes, anyway). 402 * 403 * If this is no if_spppsubr.c based interface, this ioctl just fails and we 404 * treat is as connected. 405 */ 406 static int 407 if_is_connected(const char * ifname) 408 { 409 int s, err; 410 struct spppstatus status; 411 412 memset(&status, 0, sizeof status); 413 strncpy(status.ifname, ifname, sizeof status.ifname); 414 s = socket(AF_INET, SOCK_DGRAM, 0); 415 if (s < 0) 416 return 1; /* no idea how to handle this... */ 417 err = ioctl(s, SPPPGETSTATUS, &status); 418 if (err != 0) 419 /* not if_spppsubr.c based - call it connected */ 420 status.phase = SPPP_PHASE_NETWORK; 421 close(s); 422 return status.phase == SPPP_PHASE_NETWORK; 423 } 424 #endif 425