1 /* $NetBSD: ifwatchd.c,v 1.5 2002/01/10 20:21:50 martin Exp $ */ 2 3 /* 4 * Copyright (c) 2001-2002 Martin Husemann <martin@duskware.de> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software withough specific prior written permission 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * Define this for special treatment of sys/net/if_spppsubr.c based interfaces. 29 */ 30 #define SPPP_IF_SUPPORT 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/ioctl.h> 35 #include <sys/socket.h> 36 #include <sys/queue.h> 37 #include <net/if.h> 38 #include <net/if_dl.h> 39 #ifdef SPPP_IF_SUPPORT 40 #include <net/if_sppp.h> 41 #endif 42 #include <net/route.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <netdb.h> 51 #include <err.h> 52 #include <ifaddrs.h> 53 54 /* local functions */ 55 static void usage(void); 56 static void dispatch(void*, size_t); 57 static void check_addrs(char *cp, int addrs, int is_up); 58 static void invoke_script(struct sockaddr *sa, struct sockaddr *dst, int is_up, int ifindex); 59 static void list_interfaces(const char *ifnames); 60 static void rescan_interfaces(void); 61 static void free_interfaces(void); 62 static int find_interface(int index); 63 static void run_initial_ups(void); 64 65 #ifdef SPPP_IF_SUPPORT 66 static int if_is_connected(const char * ifname); 67 #else 68 #define if_is_connected(X) 1 69 #endif 70 71 /* stolen from /sbin/route */ 72 #define ROUNDUP(a) \ 73 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 74 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 75 76 /* global variables */ 77 static int verbose = 0; 78 static int inhibit_initial = 0; 79 static const char *up_script = NULL; 80 static const char *down_script = NULL; 81 82 struct interface_data { 83 SLIST_ENTRY(interface_data) next; 84 int index; 85 char * ifname; 86 }; 87 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs); 88 89 int 90 main(int argc, char **argv) 91 { 92 int c, s, n; 93 int errs = 0; 94 char msg[2048], *msgp; 95 96 while ((c = getopt(argc, argv, "vhiu:d:")) != -1) 97 switch (c) { 98 case 'h': 99 usage(); 100 return 0; 101 case 'i': 102 inhibit_initial = 1; 103 break; 104 case 'v': 105 verbose++; 106 break; 107 108 case 'u': 109 up_script = optarg; 110 break; 111 112 case 'd': 113 down_script = optarg; 114 break; 115 116 default: 117 errs++; 118 break; 119 } 120 121 if (errs) 122 usage(); 123 124 argv += optind; 125 argc -= optind; 126 127 if (argc <= 0) 128 usage(); 129 130 if (verbose) { 131 printf("up_script: %s\ndown_script: %s\n", 132 up_script, down_script); 133 printf("verbosity = %d\n", verbose); 134 } 135 136 while (argc > 0) { 137 list_interfaces(argv[0]); 138 argv++; argc--; 139 } 140 141 if (!verbose) 142 daemon(0,0); 143 144 if (!inhibit_initial) 145 run_initial_ups(); 146 147 s = socket(PF_ROUTE, SOCK_RAW, 0); 148 if (s < 0) { 149 perror("open routing socket"); 150 exit(1); 151 } 152 153 for (;;) { 154 n = read(s, msg, sizeof msg); 155 msgp = msg; 156 for (msgp = msg; n > 0; n -= ((struct rt_msghdr*)msgp)->rtm_msglen, msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) { 157 dispatch(msgp, n); 158 159 } 160 } 161 162 close(s); 163 free_interfaces(); 164 165 exit(0); 166 } 167 168 static void 169 usage() 170 { 171 fprintf(stderr, 172 "usage:\n" 173 "\tifwatchd [-h] [-v] [-u up-script] [-d down-script] ifname(s)\n" 174 "\twhere:\n" 175 "\t -h show this help message\n" 176 "\t -v verbose/debug output, don't run in background\n" 177 "\t -i no (!) initial run of the up script if the interface\n" 178 "\t is already up on ifwatchd startup\n" 179 "\t -u <cmd> specify command to run on interface up event\n" 180 "\t -d <cmd> specify command to run on interface down event\n"); 181 exit(1); 182 } 183 184 static void 185 dispatch(void *msg, size_t len) 186 { 187 struct rt_msghdr *hd = msg; 188 struct ifa_msghdr *ifam; 189 int is_up; 190 191 is_up = 0; 192 switch (hd->rtm_type) { 193 case RTM_NEWADDR: 194 is_up = 1; 195 goto work; 196 case RTM_DELADDR: 197 is_up = 0; 198 goto work; 199 case RTM_IFANNOUNCE: 200 rescan_interfaces(); 201 break; 202 } 203 if (verbose) 204 printf("unknown message ignored\n"); 205 return; 206 207 work: 208 ifam = (struct ifa_msghdr *)msg; 209 check_addrs((char *)(ifam + 1), ifam->ifam_addrs, is_up); 210 } 211 212 static void 213 check_addrs(cp, addrs, is_up) 214 char *cp; 215 int addrs, is_up; 216 { 217 struct sockaddr *sa, *ifa = NULL, *brd = NULL; 218 int ifndx = 0, i; 219 220 if (addrs == 0) 221 return; 222 for (i = 1; i; i <<= 1) { 223 if (i & addrs) { 224 sa = (struct sockaddr *)cp; 225 if (i == RTA_IFP) { 226 struct sockaddr_dl * li = (struct sockaddr_dl*)sa; 227 ifndx = li->sdl_index; 228 if (!find_interface(ifndx)) { 229 if (verbose) 230 printf("ignoring change on interface #%d\n", ifndx); 231 return; 232 } 233 } else if (i == RTA_IFA) { 234 ifa = sa; 235 } else if (i == RTA_BRD) { 236 brd = sa; 237 } 238 ADVANCE(cp, sa); 239 } 240 } 241 if (ifa != NULL) 242 invoke_script(ifa, brd, is_up, ifndx); 243 } 244 245 static void 246 invoke_script(sa, dest, is_up, ifindex) 247 struct sockaddr *sa, *dest; 248 int is_up, ifindex; 249 { 250 char addr[NI_MAXHOST], daddr[NI_MAXHOST], ifname_buf[IFNAMSIZ], 251 *ifname, *cmd; 252 const char *script; 253 254 daddr[0] = 0; 255 ifname = if_indextoname(ifindex, ifname_buf); 256 if (sa->sa_len == 0) { 257 fprintf(stderr, "illegal socket address (sa_len == 0)\n"); 258 return; 259 } 260 261 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, NI_NUMERICHOST)) { 262 if (verbose) 263 printf("getnameinfo failed\n"); 264 return; /* this address can not be handled */ 265 } 266 if (dest != NULL) { 267 if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr, NULL, 0, NI_NUMERICHOST)) { 268 if (verbose) 269 printf("getnameinfo failed\n"); 270 return; /* this address can not be handled */ 271 } 272 } 273 274 script = is_up? up_script : down_script; 275 if (script == NULL) return; 276 277 asprintf(&cmd, "%s \"%s\" %s \"%s\" \"%s\"", script, ifname, 278 is_up?"up":"down", addr, daddr); 279 if (cmd == NULL) { 280 fprintf(stderr, "out of memory\n"); 281 return; 282 } 283 if (verbose) 284 printf("calling: %s\n", cmd); 285 system(cmd); 286 free(cmd); 287 } 288 289 static void list_interfaces(const char *ifnames) 290 { 291 char * names = strdup(ifnames); 292 char * name, *lasts; 293 static const char sep[] = " \t"; 294 struct interface_data * p; 295 296 for (name = strtok_r(names, sep, &lasts); name != NULL; name = strtok_r(NULL, sep, &lasts)) { 297 p = malloc(sizeof(*p)); 298 SLIST_INSERT_HEAD(&ifs, p, next); 299 p->ifname = strdup(name); 300 p->index = if_nametoindex(p->ifname); 301 if (verbose) 302 printf("interface \"%s\" has index %d\n", p->ifname, p->index); 303 } 304 free(names); 305 } 306 307 static void rescan_interfaces() 308 { 309 struct interface_data * p; 310 311 SLIST_FOREACH(p, &ifs, next) { 312 p->index = if_nametoindex(p->ifname); 313 if (verbose) 314 printf("interface \"%s\" has index %d\n", p->ifname, p->index); 315 } 316 } 317 318 static void free_interfaces() 319 { 320 struct interface_data * p; 321 322 while (!SLIST_EMPTY(&ifs)) { 323 p = SLIST_FIRST(&ifs); 324 SLIST_REMOVE_HEAD(&ifs, next); 325 free(p->ifname); 326 free(p); 327 } 328 } 329 330 static int find_interface(index) 331 int index; 332 { 333 struct interface_data * p; 334 335 SLIST_FOREACH(p, &ifs, next) 336 if (p->index == index) 337 return 1; 338 return 0; 339 } 340 341 static void run_initial_ups() 342 { 343 struct interface_data * ifd; 344 struct ifaddrs *res = NULL, *p; 345 346 if (getifaddrs(&res) == 0) { 347 for (p = res; p; p = p->ifa_next) { 348 if ((p->ifa_flags & IFF_UP) == 0) 349 continue; 350 if (p->ifa_addr == NULL) 351 continue; 352 if (p->ifa_addr->sa_family == AF_LINK) 353 continue; 354 SLIST_FOREACH(ifd, &ifs, next) { 355 if (strcmp(ifd->ifname, p->ifa_name) == 0) { 356 if (if_is_connected(ifd->ifname)) 357 invoke_script(p->ifa_addr, p->ifa_dstaddr, 1, ifd->index); 358 break; 359 } 360 } 361 } 362 freeifaddrs(res); 363 } 364 } 365 366 #ifdef SPPP_IF_SUPPORT 367 /* 368 * Special case support for in-kernel PPP interfaces. 369 * If these are IFF_UP, but have not yet connected or completed authentication 370 * we don't want to call the up script in the initial interface scan (there 371 * will be an UP event generated later, when IPCP completes, anyway). 372 * 373 * If this is no if_spppsubr.c based interface, this ioctl just fails and we 374 * treat is as connected. 375 */ 376 static int 377 if_is_connected(const char * ifname) 378 { 379 int s, err; 380 struct spppstatus status; 381 382 memset(&status, 0, sizeof status); 383 strncpy(status.ifname, ifname, sizeof status.ifname); 384 s = socket(AF_INET, SOCK_DGRAM, 0); 385 if (s < 0) 386 return 1; /* no idea how to handle this... */ 387 err = ioctl(s, SPPPGETSTATUS, &status); 388 if (err != 0) 389 /* not if_spppsubr.c based - call it connected */ 390 status.phase = SPPP_PHASE_NETWORK; 391 close(s); 392 return status.phase == SPPP_PHASE_NETWORK; 393 } 394 #endif 395