Home | History | Annotate | Line # | Download | only in ifwatchd
ifwatchd.c revision 1.46.6.1
      1  1.46.6.1    martin /*	$NetBSD: ifwatchd.c,v 1.46.6.1 2024/09/21 12:35:22 martin Exp $	*/
      2      1.36       roy #include <sys/cdefs.h>
      3  1.46.6.1    martin __RCSID("$NetBSD: ifwatchd.c,v 1.46.6.1 2024/09/21 12:35:22 martin Exp $");
      4       1.2    martin 
      5       1.6    martin /*-
      6      1.14    martin  * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
      7       1.1    martin  * All rights reserved.
      8       1.1    martin  *
      9       1.6    martin  * This code is derived from software contributed to The NetBSD Foundation
     10      1.17     grant  * by Martin Husemann <martin (at) NetBSD.org>.
     11       1.6    martin  *
     12       1.1    martin  * Redistribution and use in source and binary forms, with or without
     13       1.1    martin  * modification, are permitted provided that the following conditions
     14       1.1    martin  * are met:
     15       1.1    martin  * 1. Redistributions of source code must retain the above copyright
     16       1.1    martin  *    notice, this list of conditions and the following disclaimer.
     17       1.6    martin  * 2. Redistributions in binary form must reproduce the above copyright
     18       1.6    martin  *    notice, this list of conditions and the following disclaimer in the
     19       1.6    martin  *    documentation and/or other materials provided with the distribution.
     20       1.1    martin  *
     21       1.6    martin  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     22       1.6    martin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     23       1.6    martin  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     24       1.6    martin  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     25       1.6    martin  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26       1.6    martin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27       1.6    martin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28       1.6    martin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29       1.6    martin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30       1.6    martin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31       1.6    martin  * POSSIBILITY OF SUCH DAMAGE.
     32       1.1    martin  */
     33       1.1    martin 
     34       1.1    martin #include <sys/types.h>
     35       1.1    martin #include <sys/param.h>
     36       1.3    martin #include <sys/ioctl.h>
     37       1.1    martin #include <sys/socket.h>
     38       1.2    martin #include <sys/queue.h>
     39       1.9      tron #include <sys/wait.h>
     40       1.1    martin #include <net/if.h>
     41       1.1    martin #include <net/if_dl.h>
     42       1.1    martin #include <net/route.h>
     43       1.2    martin #include <netinet/in.h>
     44      1.33       roy #include <netinet/in_var.h>
     45       1.2    martin #include <arpa/inet.h>
     46       1.1    martin 
     47      1.46       roy #include <err.h>
     48      1.46       roy #include <errno.h>
     49      1.46       roy #include <ifaddrs.h>
     50      1.46       roy #include <netdb.h>
     51       1.7      tron #include <paths.h>
     52       1.1    martin #include <stdio.h>
     53       1.1    martin #include <stdlib.h>
     54       1.1    martin #include <string.h>
     55      1.46       roy #include <syslog.h>
     56       1.1    martin #include <unistd.h>
     57       1.1    martin 
     58      1.19    martin enum event { ARRIVAL, DEPARTURE, UP, DOWN, CARRIER, NO_CARRIER };
     59  1.46.6.1    martin enum addrflag { NOTREADY, DETACHED, DEPRECATED, READY };
     60      1.19    martin 
     61       1.1    martin /* local functions */
     62      1.26     joerg __dead static void usage(void);
     63      1.30       roy static void dispatch(const void *, size_t);
     64      1.33       roy static enum addrflag check_addrflags(int af, int addrflags);
     65      1.31       roy static void check_addrs(const struct ifa_msghdr *ifam);
     66      1.37       roy static void invoke_script(const char *ifname, enum event ev,
     67      1.37       roy     const struct sockaddr *sa, const struct sockaddr *dst);
     68       1.1    martin static void list_interfaces(const char *ifnames);
     69      1.30       roy static void check_announce(const struct if_announcemsghdr *ifan);
     70      1.31       roy static void check_carrier(const struct if_msghdr *ifm);
     71       1.1    martin static void free_interfaces(void);
     72      1.32       roy static struct interface_data * find_interface(int index);
     73      1.46       roy static void run_initial_ups(bool);
     74       1.1    martin 
     75       1.1    martin /* global variables */
     76      1.14    martin static int verbose = 0, quiet = 0;
     77       1.3    martin static int inhibit_initial = 0;
     78      1.10    martin static const char *arrival_script = NULL;
     79      1.10    martin static const char *departure_script = NULL;
     80       1.1    martin static const char *up_script = NULL;
     81       1.1    martin static const char *down_script = NULL;
     82      1.19    martin static const char *carrier_script = NULL;
     83      1.19    martin static const char *no_carrier_script = NULL;
     84      1.19    martin static const char DummyTTY[] = _PATH_DEVNULL;
     85      1.19    martin static const char DummySpeed[] = "9600";
     86      1.10    martin static const char **scripts[] = {
     87      1.10    martin 	&arrival_script,
     88      1.10    martin 	&departure_script,
     89      1.10    martin 	&up_script,
     90      1.19    martin 	&down_script,
     91      1.19    martin 	&carrier_script,
     92      1.19    martin 	&no_carrier_script
     93      1.13    itojun };
     94       1.1    martin 
     95       1.1    martin struct interface_data {
     96       1.1    martin 	SLIST_ENTRY(interface_data) next;
     97       1.1    martin 	int index;
     98      1.19    martin 	int last_carrier_status;
     99       1.1    martin 	char * ifname;
    100       1.1    martin };
    101      1.26     joerg static SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs);
    102       1.1    martin 
    103       1.1    martin int
    104       1.1    martin main(int argc, char **argv)
    105       1.1    martin {
    106       1.1    martin 	int c, s, n;
    107       1.1    martin 	int errs = 0;
    108      1.28       roy 	struct msghdr msg;
    109      1.28       roy 	struct iovec iov[1];
    110      1.28       roy 	char buf[2048];
    111      1.42       roy 	unsigned char msgfilter[] = {
    112      1.42       roy 		RTM_IFINFO, RTM_IFANNOUNCE,
    113      1.42       roy 		RTM_NEWADDR, RTM_DELADDR,
    114      1.42       roy 	};
    115       1.1    martin 
    116      1.14    martin 	openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON);
    117      1.21    dyoung 	while ((c = getopt(argc, argv, "qvhic:n:u:d:A:D:")) != -1) {
    118       1.1    martin 		switch (c) {
    119       1.1    martin 		case 'h':
    120       1.1    martin 			usage();
    121       1.1    martin 			return 0;
    122      1.19    martin 
    123       1.3    martin 		case 'i':
    124       1.3    martin 			inhibit_initial = 1;
    125       1.3    martin 			break;
    126      1.19    martin 
    127       1.1    martin 		case 'v':
    128       1.1    martin 			verbose++;
    129       1.1    martin 			break;
    130      1.19    martin 
    131      1.14    martin 		case 'q':
    132      1.14    martin 			quiet = 1;
    133      1.14    martin 			break;
    134       1.1    martin 
    135      1.19    martin 		case 'c':
    136      1.19    martin 			carrier_script = optarg;
    137      1.19    martin 			break;
    138      1.19    martin 
    139      1.19    martin 		case 'n':
    140      1.19    martin 			no_carrier_script = optarg;
    141      1.19    martin 			break;
    142      1.19    martin 
    143       1.1    martin 		case 'u':
    144       1.1    martin 			up_script = optarg;
    145       1.1    martin 			break;
    146       1.1    martin 
    147       1.1    martin 		case 'd':
    148       1.1    martin 			down_script = optarg;
    149       1.1    martin 			break;
    150       1.1    martin 
    151      1.10    martin 		case 'A':
    152      1.10    martin 			arrival_script = optarg;
    153      1.10    martin 			break;
    154      1.10    martin 
    155      1.10    martin 		case 'D':
    156      1.10    martin 			departure_script = optarg;
    157      1.10    martin 			break;
    158      1.10    martin 
    159       1.1    martin 		default:
    160       1.1    martin 			errs++;
    161       1.1    martin 			break;
    162       1.1    martin 		}
    163      1.21    dyoung 	}
    164       1.1    martin 
    165       1.1    martin 	if (errs)
    166       1.1    martin 		usage();
    167       1.1    martin 
    168       1.1    martin 	argv += optind;
    169       1.1    martin 	argc -= optind;
    170       1.1    martin 
    171      1.21    dyoung 	if (argc <= 0)
    172       1.1    martin 		usage();
    173       1.1    martin 
    174       1.1    martin 	if (verbose) {
    175       1.1    martin 		printf("up_script: %s\ndown_script: %s\n",
    176       1.1    martin 			up_script, down_script);
    177      1.10    martin 		printf("arrival_script: %s\ndeparture_script: %s\n",
    178      1.10    martin 			arrival_script, departure_script);
    179      1.19    martin 		printf("carrier_script: %s\nno_carrier_script: %s\n",
    180      1.19    martin 			carrier_script, no_carrier_script);
    181       1.1    martin 		printf("verbosity = %d\n", verbose);
    182       1.1    martin 	}
    183       1.1    martin 
    184       1.1    martin 	while (argc > 0) {
    185      1.13    itojun 		list_interfaces(argv[0]);
    186      1.13    itojun 		argv++;
    187      1.13    itojun 		argc--;
    188       1.1    martin 	}
    189       1.1    martin 
    190       1.1    martin 	if (!verbose)
    191      1.21    dyoung 		daemon(0, 0);
    192       1.1    martin 
    193       1.1    martin 	s = socket(PF_ROUTE, SOCK_RAW, 0);
    194       1.1    martin 	if (s < 0) {
    195      1.14    martin 		syslog(LOG_ERR, "error opening routing socket: %m");
    196       1.9      tron 		exit(EXIT_FAILURE);
    197       1.1    martin 	}
    198      1.42       roy 	if (setsockopt(s, PF_ROUTE, RO_MSGFILTER,
    199      1.42       roy 	    &msgfilter, sizeof(msgfilter)) < 0)
    200      1.42       roy 		syslog(LOG_ERR, "RO_MSGFILTER: %m");
    201      1.46       roy 	n = 1;
    202      1.46       roy 	if (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) < 0)
    203      1.46       roy 		syslog(LOG_ERR, "SO_RERROR: %m");
    204       1.1    martin 
    205      1.11    martin 	if (!inhibit_initial)
    206      1.46       roy 		run_initial_ups(true);
    207      1.11    martin 
    208      1.28       roy 	iov[0].iov_base = buf;
    209      1.28       roy 	iov[0].iov_len = sizeof(buf);
    210      1.28       roy 	memset(&msg, 0, sizeof(msg));
    211      1.28       roy 	msg.msg_iov = iov;
    212      1.28       roy 	msg.msg_iovlen = 1;
    213      1.28       roy 
    214       1.1    martin 	for (;;) {
    215      1.28       roy 		n = recvmsg(s, &msg, 0);
    216      1.28       roy 		if (n == -1) {
    217      1.46       roy 			if (errno == ENOBUFS) {
    218      1.46       roy 				syslog(LOG_ERR,
    219      1.46       roy 				    "routing socket overflow detected");
    220      1.46       roy 				/* XXX We don't track addresses, so they
    221      1.46       roy 				 * won't be reported. */
    222      1.46       roy 				if (!inhibit_initial)
    223      1.46       roy 					run_initial_ups(false);
    224      1.46       roy 				continue;
    225      1.46       roy 			}
    226      1.28       roy 			syslog(LOG_ERR, "recvmsg: %m");
    227      1.28       roy 			exit(EXIT_FAILURE);
    228      1.28       roy 		}
    229      1.28       roy 		if (n != 0)
    230      1.28       roy 			dispatch(iov[0].iov_base, n);
    231       1.1    martin 	}
    232       1.1    martin 
    233       1.1    martin 	close(s);
    234       1.1    martin 	free_interfaces();
    235      1.14    martin 	closelog();
    236       1.1    martin 
    237       1.9      tron 	return EXIT_SUCCESS;
    238       1.1    martin }
    239       1.1    martin 
    240       1.1    martin static void
    241      1.21    dyoung usage(void)
    242       1.1    martin {
    243      1.21    dyoung 	fprintf(stderr,
    244       1.1    martin 	    "usage:\n"
    245      1.15       wiz 	    "\tifwatchd [-hiqv] [-A arrival-script] [-D departure-script]\n"
    246      1.19    martin 	    "\t\t  [-d down-script] [-u up-script]\n"
    247      1.19    martin 	    "\t\t  [-c carrier-script] [-n no-carrier-script] ifname(s)\n"
    248       1.1    martin 	    "\twhere:\n"
    249      1.12       wiz 	    "\t -A <cmd> specify command to run on interface arrival event\n"
    250      1.19    martin 	    "\t -c <cmd> specify command to run on interface carrier-detect event\n"
    251      1.12       wiz 	    "\t -D <cmd> specify command to run on interface departure event\n"
    252      1.12       wiz 	    "\t -d <cmd> specify command to run on interface down event\n"
    253      1.19    martin 	    "\t -n <cmd> specify command to run on interface no-carrier-detect event\n"
    254       1.1    martin 	    "\t -h       show this help message\n"
    255       1.3    martin 	    "\t -i       no (!) initial run of the up script if the interface\n"
    256       1.3    martin 	    "\t          is already up on ifwatchd startup\n"
    257      1.16       abs 	    "\t -q       quiet mode, don't syslog informational messages\n"
    258       1.1    martin 	    "\t -u <cmd> specify command to run on interface up event\n"
    259      1.16       abs 	    "\t -v       verbose/debug output, don't run in background\n");
    260       1.9      tron 	exit(EXIT_FAILURE);
    261       1.1    martin }
    262       1.1    martin 
    263       1.1    martin static void
    264      1.30       roy dispatch(const void *msg, size_t len)
    265       1.1    martin {
    266      1.30       roy 	const struct rt_msghdr *hd = msg;
    267       1.1    martin 
    268      1.29       roy 	if (hd->rtm_version != RTM_VERSION)
    269      1.29       roy 		return;
    270      1.29       roy 
    271       1.1    martin 	switch (hd->rtm_type) {
    272       1.1    martin 	case RTM_NEWADDR:
    273       1.1    martin 	case RTM_DELADDR:
    274      1.31       roy 		check_addrs(msg);
    275      1.31       roy 		break;
    276       1.1    martin 	case RTM_IFANNOUNCE:
    277      1.30       roy 		check_announce(msg);
    278      1.31       roy 		break;
    279      1.19    martin 	case RTM_IFINFO:
    280      1.31       roy 		check_carrier(msg);
    281      1.31       roy 		break;
    282      1.31       roy 	default:
    283      1.43       roy 		/* Should be impossible as we filter messages. */
    284      1.31       roy 		if (verbose)
    285      1.31       roy 			printf("unknown message ignored (%d)\n", hd->rtm_type);
    286      1.31       roy 		break;
    287       1.1    martin 	}
    288       1.1    martin }
    289       1.1    martin 
    290      1.33       roy static enum addrflag
    291      1.33       roy check_addrflags(int af, int addrflags)
    292      1.33       roy {
    293      1.33       roy 
    294      1.33       roy 	switch (af) {
    295      1.33       roy 	case AF_INET:
    296      1.33       roy 		if (addrflags & IN_IFF_NOTREADY)
    297      1.33       roy 			return NOTREADY;
    298      1.33       roy 		if (addrflags & IN_IFF_DETACHED)
    299      1.33       roy 			return DETACHED;
    300      1.33       roy 		break;
    301      1.33       roy 	case AF_INET6:
    302      1.33       roy 		if (addrflags & IN6_IFF_NOTREADY)
    303      1.33       roy 			return NOTREADY;
    304      1.33       roy 		if (addrflags & IN6_IFF_DETACHED)
    305      1.33       roy 			return DETACHED;
    306  1.46.6.1    martin 		if (addrflags & IN6_IFF_DEPRECATED)
    307  1.46.6.1    martin 			return DEPRECATED;
    308      1.33       roy 		break;
    309      1.33       roy 	}
    310      1.33       roy 	return READY;
    311      1.33       roy }
    312      1.33       roy 
    313       1.1    martin static void
    314      1.31       roy check_addrs(const struct ifa_msghdr *ifam)
    315       1.1    martin {
    316      1.31       roy 	const char *cp = (const char *)(ifam + 1);
    317      1.30       roy 	const struct sockaddr *sa, *ifa = NULL, *brd = NULL;
    318      1.27  riastrad 	unsigned i;
    319      1.32       roy 	struct interface_data *ifd = NULL;
    320      1.33       roy 	int aflag;
    321      1.31       roy 	enum event ev;
    322       1.1    martin 
    323      1.31       roy 	if (ifam->ifam_addrs == 0)
    324      1.13    itojun 		return;
    325       1.1    martin 	for (i = 1; i; i <<= 1) {
    326      1.31       roy 		if ((i & ifam->ifam_addrs) == 0)
    327      1.21    dyoung 			continue;
    328      1.30       roy 		sa = (const struct sockaddr *)cp;
    329      1.21    dyoung 		if (i == RTA_IFP) {
    330      1.30       roy 			const struct sockaddr_dl *li;
    331      1.30       roy 
    332      1.30       roy 			li = (const struct sockaddr_dl *)sa;
    333      1.32       roy 			if ((ifd = find_interface(li->sdl_index)) == NULL) {
    334      1.21    dyoung 				if (verbose)
    335      1.32       roy 					printf("ignoring change"
    336      1.32       roy 					    " on interface #%d\n",
    337      1.32       roy 					    li->sdl_index);
    338      1.21    dyoung 				return;
    339      1.13    itojun 			}
    340      1.21    dyoung 		} else if (i == RTA_IFA)
    341      1.21    dyoung 			ifa = sa;
    342      1.21    dyoung 		else if (i == RTA_BRD)
    343      1.21    dyoung 			brd = sa;
    344      1.25    martin 		RT_ADVANCE(cp, sa);
    345       1.1    martin 	}
    346      1.32       roy 	if (ifa != NULL && ifd != NULL) {
    347      1.31       roy 		ev = ifam->ifam_type == RTM_DELADDR ? DOWN : UP;
    348      1.33       roy 		aflag = check_addrflags(ifa->sa_family, ifam->ifam_addrflags);
    349      1.38       roy 		if ((ev == UP && aflag == READY) || ev == DOWN)
    350      1.37       roy 			invoke_script(ifd->ifname, ev, ifa, brd);
    351      1.18    martin 	}
    352       1.1    martin }
    353       1.1    martin 
    354       1.1    martin static void
    355      1.37       roy invoke_script(const char *ifname, enum event ev,
    356      1.37       roy     const struct sockaddr *sa, const struct sockaddr *dest)
    357       1.1    martin {
    358      1.37       roy 	char addr[NI_MAXHOST], daddr[NI_MAXHOST];
    359       1.1    martin 	const char *script;
    360       1.9      tron 	int status;
    361       1.8      tron 
    362      1.37       roy 	if (ifname == NULL)
    363      1.37       roy 		return;
    364      1.37       roy 
    365      1.37       roy 	script = *scripts[ev];
    366      1.37       roy 	if (script == NULL)
    367      1.37       roy 		return;
    368      1.37       roy 
    369      1.37       roy 	addr[0] = daddr[0] = 0;
    370      1.34       roy 	if (sa != NULL) {
    371      1.41  christos 		const struct sockaddr_in *sin;
    372      1.41  christos 		const struct sockaddr_in6 *sin6;
    373      1.41  christos 
    374      1.34       roy 		if (sa->sa_len == 0) {
    375      1.41  christos 			syslog(LOG_ERR,
    376      1.41  christos 			    "illegal socket address (sa_len == 0)");
    377      1.34       roy 			return;
    378      1.34       roy 		}
    379      1.34       roy 		switch (sa->sa_family) {
    380      1.34       roy 		case AF_INET:
    381      1.34       roy 			sin = (const struct sockaddr_in *)sa;
    382      1.34       roy 			if (sin->sin_addr.s_addr == INADDR_ANY ||
    383      1.34       roy 			    sin->sin_addr.s_addr == INADDR_BROADCAST)
    384      1.34       roy 				return;
    385      1.41  christos 			break;
    386      1.34       roy 		case AF_INET6:
    387      1.34       roy 			sin6 = (const struct sockaddr_in6 *)sa;
    388      1.34       roy 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
    389      1.34       roy 				return;
    390      1.41  christos 			break;
    391      1.41  christos 		default:
    392      1.41  christos 			break;
    393      1.34       roy 		}
    394       1.1    martin 
    395      1.13    itojun 		if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0,
    396      1.13    itojun 		    NI_NUMERICHOST)) {
    397      1.13    itojun 			if (verbose)
    398      1.13    itojun 				printf("getnameinfo failed\n");
    399      1.13    itojun 			return;	/* this address can not be handled */
    400      1.13    itojun 		}
    401      1.10    martin 	}
    402      1.37       roy 
    403       1.4    martin 	if (dest != NULL) {
    404      1.13    itojun 		if (getnameinfo(dest, dest->sa_len, daddr, sizeof daddr,
    405      1.13    itojun 		    NULL, 0, NI_NUMERICHOST)) {
    406      1.13    itojun 			if (verbose)
    407      1.13    itojun 				printf("getnameinfo failed\n");
    408      1.13    itojun 			return;	/* this address can not be handled */
    409      1.13    itojun 		}
    410       1.4    martin 	}
    411       1.1    martin 
    412       1.9      tron 	if (verbose)
    413      1.13    itojun 		(void) printf("calling: %s %s %s %s %s %s\n",
    414      1.13    itojun 		    script, ifname, DummyTTY, DummySpeed, addr, daddr);
    415      1.14    martin 	if (!quiet)
    416      1.14    martin 		syslog(LOG_INFO, "calling: %s %s %s %s %s %s\n",
    417      1.14    martin 		    script, ifname, DummyTTY, DummySpeed, addr, daddr);
    418       1.9      tron 
    419       1.9      tron 	switch (vfork()) {
    420       1.9      tron 	case -1:
    421      1.41  christos 		syslog(LOG_ERR, "cannot fork: %m");
    422      1.13    itojun 		break;
    423       1.9      tron 	case 0:
    424      1.14    martin 		if (execl(script, script, ifname, DummyTTY, DummySpeed,
    425      1.14    martin 		    addr, daddr, NULL) == -1) {
    426      1.14    martin 			syslog(LOG_ERR, "could not execute \"%s\": %m",
    427      1.14    martin 			    script);
    428      1.14    martin 		}
    429      1.13    itojun 		_exit(EXIT_FAILURE);
    430       1.9      tron 	default:
    431      1.13    itojun 		(void) wait(&status);
    432       1.1    martin 	}
    433       1.1    martin }
    434       1.1    martin 
    435      1.19    martin static void
    436      1.19    martin list_interfaces(const char *ifnames)
    437       1.1    martin {
    438       1.1    martin 	char * names = strdup(ifnames);
    439       1.1    martin 	char * name, *lasts;
    440       1.1    martin 	static const char sep[] = " \t";
    441       1.1    martin 	struct interface_data * p;
    442       1.1    martin 
    443      1.13    itojun 	for (name = strtok_r(names, sep, &lasts);
    444      1.13    itojun 	    name != NULL;
    445      1.13    itojun 	    name = strtok_r(NULL, sep, &lasts)) {
    446      1.13    itojun 		p = malloc(sizeof(*p));
    447      1.13    itojun 		SLIST_INSERT_HEAD(&ifs, p, next);
    448      1.19    martin 		p->last_carrier_status = -1;
    449      1.13    itojun 		p->ifname = strdup(name);
    450      1.13    itojun 		p->index = if_nametoindex(p->ifname);
    451      1.14    martin 		if (!quiet)
    452      1.14    martin 			syslog(LOG_INFO, "watching interface %s", p->ifname);
    453      1.13    itojun 		if (verbose)
    454      1.13    itojun 			printf("interface \"%s\" has index %d\n",
    455      1.13    itojun 			    p->ifname, p->index);
    456       1.1    martin 	}
    457       1.1    martin 	free(names);
    458       1.1    martin }
    459       1.1    martin 
    460      1.10    martin static void
    461      1.31       roy check_carrier(const struct if_msghdr *ifm)
    462      1.19    martin {
    463      1.19    martin 	struct interface_data * p;
    464      1.31       roy 	int carrier_status;
    465      1.19    martin 	enum event ev;
    466      1.19    martin 
    467      1.19    martin 	SLIST_FOREACH(p, &ifs, next)
    468      1.31       roy 		if (p->index == ifm->ifm_index)
    469      1.19    martin 			break;
    470      1.19    martin 
    471      1.19    martin 	if (p == NULL)
    472      1.19    martin 		return;
    473      1.19    martin 
    474      1.21    dyoung 	/*
    475      1.19    martin 	 * Treat it as an event worth handling if:
    476      1.19    martin 	 * - the carrier status changed, or
    477      1.19    martin 	 * - this is the first time we've been called, and
    478      1.19    martin 	 * inhibit_initial is not set
    479      1.19    martin 	 */
    480      1.31       roy 	carrier_status = ifm->ifm_data.ifi_link_state;
    481      1.40       roy 	if (carrier_status != p->last_carrier_status) {
    482      1.19    martin 		switch (carrier_status) {
    483      1.19    martin 		case LINK_STATE_UP:
    484      1.19    martin 			ev = CARRIER;
    485      1.19    martin 			break;
    486      1.19    martin 		case LINK_STATE_DOWN:
    487      1.19    martin 			ev = NO_CARRIER;
    488      1.19    martin 			break;
    489      1.19    martin 		default:
    490      1.19    martin 			if (verbose)
    491      1.19    martin 				printf("unknown link status ignored\n");
    492      1.19    martin 			return;
    493      1.19    martin 		}
    494      1.37       roy 		invoke_script(p->ifname, ev, NULL, NULL);
    495      1.19    martin 		p->last_carrier_status = carrier_status;
    496      1.19    martin 	}
    497      1.19    martin }
    498      1.19    martin 
    499      1.19    martin static void
    500      1.46       roy do_announce(struct interface_data *ifd,
    501      1.46       roy     unsigned short what, unsigned short index)
    502      1.46       roy {
    503      1.46       roy 
    504      1.46       roy 	switch (what) {
    505      1.46       roy 	case IFAN_ARRIVAL:
    506      1.46       roy 		ifd->index = index;
    507      1.46       roy 		invoke_script(ifd->ifname, ARRIVAL, NULL, NULL);
    508      1.46       roy 		break;
    509      1.46       roy 	case IFAN_DEPARTURE:
    510      1.46       roy 		ifd->index = -1;
    511      1.46       roy 		ifd->last_carrier_status = -1;
    512      1.46       roy 		invoke_script(ifd->ifname, DEPARTURE, NULL, NULL);
    513      1.46       roy 		break;
    514      1.46       roy 	default:
    515      1.46       roy 		if (verbose)
    516      1.46       roy 			(void) printf("unknown announce: what=%d\n", what);
    517      1.46       roy 		break;
    518      1.46       roy 	}
    519      1.46       roy }
    520      1.46       roy 
    521      1.46       roy static void
    522      1.30       roy check_announce(const struct if_announcemsghdr *ifan)
    523      1.10    martin {
    524      1.10    martin 	struct interface_data * p;
    525      1.10    martin 	const char *ifname = ifan->ifan_name;
    526      1.10    martin 
    527      1.10    martin 	SLIST_FOREACH(p, &ifs, next) {
    528      1.21    dyoung 		if (strcmp(p->ifname, ifname) != 0)
    529      1.21    dyoung 			continue;
    530      1.21    dyoung 
    531      1.46       roy 		do_announce(p, ifan->ifan_what, ifan->ifan_index);
    532      1.21    dyoung 		return;
    533      1.10    martin 	}
    534      1.10    martin }
    535      1.10    martin 
    536      1.21    dyoung static void
    537      1.21    dyoung free_interfaces(void)
    538       1.1    martin {
    539       1.1    martin 	struct interface_data * p;
    540       1.1    martin 
    541       1.1    martin 	while (!SLIST_EMPTY(&ifs)) {
    542      1.13    itojun 		p = SLIST_FIRST(&ifs);
    543      1.13    itojun 		SLIST_REMOVE_HEAD(&ifs, next);
    544      1.13    itojun 		free(p->ifname);
    545      1.13    itojun 		free(p);
    546       1.1    martin 	}
    547       1.1    martin }
    548       1.1    martin 
    549      1.32       roy static struct interface_data *
    550      1.24     lukem find_interface(int idx)
    551       1.1    martin {
    552       1.1    martin 	struct interface_data * p;
    553      1.21    dyoung 
    554       1.1    martin 	SLIST_FOREACH(p, &ifs, next)
    555      1.24     lukem 		if (p->index == idx)
    556      1.32       roy 			return p;
    557      1.32       roy 	return NULL;
    558       1.3    martin }
    559       1.3    martin 
    560      1.21    dyoung static void
    561      1.46       roy run_initial_ups(bool do_addrs)
    562       1.3    martin {
    563       1.3    martin 	struct interface_data * ifd;
    564       1.3    martin 	struct ifaddrs *res = NULL, *p;
    565      1.33       roy 	struct sockaddr *ifa;
    566      1.45       roy 	const struct if_data *ifi;
    567      1.33       roy 	int s, aflag;
    568      1.20    martin 
    569      1.20    martin 	s = socket(AF_INET, SOCK_DGRAM, 0);
    570      1.20    martin 	if (s < 0)
    571      1.20    martin 		return;
    572       1.3    martin 
    573      1.21    dyoung 	if (getifaddrs(&res) != 0)
    574      1.21    dyoung 		goto out;
    575      1.21    dyoung 
    576      1.46       roy 	/* Check if any interfaces vanished */
    577      1.46       roy 	SLIST_FOREACH(ifd, &ifs, next) {
    578      1.46       roy 		for (p = res; p; p = p->ifa_next) {
    579      1.46       roy 			if (strcmp(ifd->ifname, p->ifa_name) != 0)
    580      1.46       roy 				continue;
    581      1.46       roy 			ifa = p->ifa_addr;
    582      1.46       roy 			if (ifa != NULL && ifa->sa_family == AF_LINK)
    583      1.46       roy 				break;
    584      1.46       roy 		}
    585      1.46       roy 		if (p == NULL)
    586      1.46       roy 			do_announce(ifd, IFAN_DEPARTURE, ifd->index);
    587      1.46       roy 	}
    588      1.46       roy 
    589      1.21    dyoung 	for (p = res; p; p = p->ifa_next) {
    590      1.21    dyoung 		SLIST_FOREACH(ifd, &ifs, next) {
    591      1.21    dyoung 			if (strcmp(ifd->ifname, p->ifa_name) == 0)
    592      1.21    dyoung 				break;
    593      1.21    dyoung 		}
    594      1.21    dyoung 		if (ifd == NULL)
    595      1.21    dyoung 			continue;
    596      1.13    itojun 
    597      1.33       roy 		ifa = p->ifa_addr;
    598      1.46       roy 		if (ifa != NULL && ifa->sa_family == AF_LINK &&
    599      1.46       roy 		    ifd->index == -1)
    600      1.37       roy 			invoke_script(ifd->ifname, ARRIVAL, NULL, NULL);
    601      1.21    dyoung 
    602      1.21    dyoung 		if ((p->ifa_flags & IFF_UP) == 0)
    603      1.21    dyoung 			continue;
    604      1.33       roy 		if (ifa == NULL)
    605      1.21    dyoung 			continue;
    606      1.33       roy 		if (ifa->sa_family == AF_LINK) {
    607      1.45       roy 			ifi = (const struct if_data *)p->ifa_data;
    608      1.46       roy 			if (ifd->last_carrier_status == ifi->ifi_link_state)
    609      1.46       roy 				continue;
    610      1.46       roy 			switch (ifi->ifi_link_state) {
    611      1.46       roy 			case LINK_STATE_UP:
    612      1.46       roy 				invoke_script(ifd->ifname, CARRIER, NULL, NULL);
    613      1.46       roy 				break;
    614      1.46       roy 			case LINK_STATE_DOWN:
    615      1.46       roy 				if (ifd->last_carrier_status == -1)
    616      1.46       roy 					break;
    617      1.37       roy 				invoke_script(ifd->ifname, CARRIER, NULL, NULL);
    618      1.46       roy 				break;
    619      1.46       roy 			}
    620      1.45       roy 			ifd->last_carrier_status = ifi->ifi_link_state;
    621      1.21    dyoung 			continue;
    622      1.21    dyoung 		}
    623      1.46       roy 		if (!do_addrs)
    624      1.46       roy 			continue;
    625      1.33       roy 		aflag = check_addrflags(ifa->sa_family, p->ifa_addrflags);
    626      1.33       roy 		if (aflag != READY)
    627      1.33       roy 			continue;
    628      1.37       roy 		invoke_script(ifd->ifname, UP, ifa, p->ifa_dstaddr);
    629       1.3    martin 	}
    630      1.21    dyoung 	freeifaddrs(res);
    631      1.21    dyoung out:
    632      1.20    martin 	close(s);
    633       1.1    martin }
    634