Home | History | Annotate | Line # | Download | only in ifwatchd
ifwatchd.c revision 1.1
      1 /*
      2  * Copyright (c) 2001 Martin Husemann <martin (at) duskware.de>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. The name of the author may not be used to endorse or promote products
     11  *    derived from this software withough specific prior written permission
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 
     26 #include <sys/types.h>
     27 #include <sys/param.h>
     28 #include <sys/file.h>
     29 #include <sys/socket.h>
     30 #include <sys/ioctl.h>
     31 #include <sys/mbuf.h>
     32 #include <sys/sysctl.h>
     33 
     34 #include <net/if.h>
     35 #include <net/if_dl.h>
     36 #include <net/route.h>
     37 
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <sysexits.h>
     42 #include <unistd.h>
     43 
     44 #include <netinet/in.h>
     45 #include <arpa/inet.h>
     46 #include <netdb.h>
     47 
     48 #include <sys/queue.h>
     49 
     50 /* local functions */
     51 static void usage(void);
     52 static void dispatch(void*, size_t);
     53 static void check_addrs(char *cp, int addrs, int is_up);
     54 static void invoke_script(struct sockaddr *sa, int is_up, int ifindex);
     55 static void list_interfaces(const char *ifnames);
     56 static void rescan_interfaces(void);
     57 static void free_interfaces(void);
     58 static int find_interface(int index);
     59 
     60 /* stolen from /sbin/route */
     61 #define ROUNDUP(a) \
     62 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
     63 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
     64 
     65 /* global variables */
     66 static int verbose = 0;
     67 static const char *up_script = NULL;
     68 static const char *down_script = NULL;
     69 
     70 struct interface_data {
     71 	SLIST_ENTRY(interface_data) next;
     72 	int index;
     73 	char * ifname;
     74 };
     75 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs);
     76 
     77 int
     78 main(int argc, char **argv)
     79 {
     80 	int c, s, n;
     81 	int errs = 0;
     82 	char msg[2048], *msgp;
     83 
     84 	while ((c = getopt(argc, argv, "vhu:d:")) != -1)
     85 		switch (c) {
     86 		case 'h':
     87 			usage();
     88 			return 0;
     89 		case 'v':
     90 			verbose++;
     91 			break;
     92 
     93 		case 'u':
     94 			up_script = optarg;
     95 			break;
     96 
     97 		case 'd':
     98 			down_script = optarg;
     99 			break;
    100 
    101 		default:
    102 			errs++;
    103 			break;
    104 		}
    105 
    106 	if (errs)
    107 		usage();
    108 
    109 	argv += optind;
    110 	argc -= optind;
    111 
    112 	if (argc <= 0)
    113 		usage();
    114 
    115 	if (verbose) {
    116 		printf("up_script: %s\ndown_script: %s\n",
    117 			up_script, down_script);
    118 		printf("verbosity = %d\n", verbose);
    119 	}
    120 
    121 	while (argc > 0) {
    122 	    list_interfaces(argv[0]);
    123 	    argv++; argc--;
    124 	}
    125 
    126 	if (!verbose)
    127 		daemon(0,0);
    128 
    129 	s = socket(PF_ROUTE, SOCK_RAW, 0);
    130 	if (s < 0) {
    131 		perror("open routing socket");
    132 		exit(1);
    133 	}
    134 
    135 	for (;;) {
    136 		n = read(s, msg, sizeof msg);
    137 		msgp = msg;
    138 		for (msgp = msg; n > 0; n -= ((struct rt_msghdr*)msgp)->rtm_msglen, msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) {
    139 			dispatch(msgp, n);
    140 
    141 		}
    142 	}
    143 
    144 	close(s);
    145 	free_interfaces();
    146 
    147 	exit(0);
    148 }
    149 
    150 static void
    151 usage()
    152 {
    153 	fprintf(stderr,
    154 	    "usage:\n"
    155 	    "\tifwatchd [-h] [-v] [-u up-script] [-d down-script] ifname(s)\n"
    156 	    "\twhere:\n"
    157 	    "\t -h       show this help message\n"
    158 	    "\t -v       verbose/debug output, don't run in background\n"
    159 	    "\t -u <cmd> specify command to run on interface up event\n"
    160 	    "\t -d <cmd> specify command to run on interface down event\n");
    161 	exit(1);
    162 }
    163 
    164 static void
    165 dispatch(void *msg, size_t len)
    166 {
    167 	struct rt_msghdr *hd = msg;
    168 	struct ifa_msghdr *ifam;
    169 	int is_up;
    170 
    171 	is_up = 0;
    172 	switch (hd->rtm_type) {
    173 	case RTM_NEWADDR:
    174 		is_up = 1;
    175 		goto work;
    176 	case RTM_DELADDR:
    177 		is_up = 0;
    178 		goto work;
    179 	case RTM_IFANNOUNCE:
    180 		rescan_interfaces();
    181 		break;
    182 	}
    183 	if (verbose)
    184 		printf("unknown message ignored\n");
    185 	return;
    186 
    187 work:
    188 	ifam = (struct ifa_msghdr *)msg;
    189 	check_addrs((char *)(ifam + 1), ifam->ifam_addrs, is_up);
    190 }
    191 
    192 static void
    193 check_addrs(cp, addrs, is_up)
    194 	char    *cp;
    195 	int     addrs, is_up;
    196 {
    197 	struct sockaddr *sa;
    198 	int ifndx = 0, i;
    199 
    200 	if (addrs == 0)
    201 	    return;
    202 	for (i = 1; i; i <<= 1) {
    203 	    if (i & addrs) {
    204 		sa = (struct sockaddr *)cp;
    205 		if (i == RTA_IFP) {
    206 		    struct sockaddr_dl * li = (struct sockaddr_dl*)sa;
    207 		    ifndx = li->sdl_index;
    208 		    if (!find_interface(ifndx)) {
    209 			if (verbose)
    210 			    printf("ignoring change on interface #%d\n", ifndx);
    211 			return;
    212 		    }
    213 		} else if (i == RTA_IFA) {
    214 		    invoke_script(sa, is_up, ifndx);
    215 		}
    216 		ADVANCE(cp, sa);
    217 	    }
    218 	}
    219 }
    220 
    221 static void
    222 invoke_script(sa, is_up, ifindex)
    223 	struct sockaddr *sa;
    224 	int is_up, ifindex;
    225 {
    226 	char addr[NI_MAXHOST], ifname_buf[IFNAMSIZ], *ifname, *cmd;
    227 	const char *script;
    228 
    229 	ifname = if_indextoname(ifindex, ifname_buf);
    230 	if (sa->sa_len == 0) {
    231 	    fprintf(stderr, "illegal socket address (sa_len == 0)\n");
    232 	    return;
    233 	}
    234 
    235 	if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, NI_NUMERICHOST)) {
    236 	    if (verbose)
    237 		printf("getnameinfo failed\n");
    238 	    return;	/* this address can not be handled */
    239 	}
    240 
    241 	script = is_up? up_script : down_script;
    242 	if (script == NULL) return;
    243 
    244 	asprintf(&cmd, "%s \"%s\" %s \"%s\"", script, ifname,
    245 			is_up?"up":"down", addr);
    246 	if (cmd == NULL) {
    247 	    fprintf(stderr, "out of memory\n");
    248 	    return;
    249 	}
    250 	if (verbose)
    251 	    printf("calling: %s\n", cmd);
    252 	system(cmd);
    253 	free(cmd);
    254 }
    255 
    256 static void list_interfaces(const char *ifnames)
    257 {
    258 	char * names = strdup(ifnames);
    259 	char * name, *lasts;
    260 	static const char sep[] = " \t";
    261 	struct interface_data * p;
    262 
    263 	for (name = strtok_r(names, sep, &lasts); name != NULL; name = strtok_r(NULL, sep, &lasts)) {
    264 	    p = malloc(sizeof(*p));
    265 	    SLIST_INSERT_HEAD(&ifs, p, next);
    266 	    p->ifname = strdup(name);
    267 	    p->index = if_nametoindex(p->ifname);
    268 	    if (verbose)
    269 		printf("interface \"%s\" has index %d\n", p->ifname, p->index);
    270 	}
    271 	free(names);
    272 }
    273 
    274 static void rescan_interfaces()
    275 {
    276 	struct interface_data * p;
    277 
    278 	SLIST_FOREACH(p, &ifs, next) {
    279 	    p->index = if_nametoindex(p->ifname);
    280 	    if (verbose)
    281 		printf("interface \"%s\" has index %d\n", p->ifname, p->index);
    282 	}
    283 }
    284 
    285 static void free_interfaces()
    286 {
    287 	struct interface_data * p;
    288 
    289 	while (!SLIST_EMPTY(&ifs)) {
    290 	    p = SLIST_FIRST(&ifs);
    291 	    SLIST_REMOVE_HEAD(&ifs, next);
    292 	    free(p->ifname);
    293 	    free(p);
    294 	}
    295 }
    296 
    297 static int find_interface(index)
    298 	int index;
    299 {
    300 	struct interface_data * p;
    301 
    302 	SLIST_FOREACH(p, &ifs, next)
    303 	    if (p->index == index)
    304 		return 1;
    305 	return 0;
    306 }
    307