ifwatchd.c revision 1.2 1 /* $NetBSD: ifwatchd.c,v 1.2 2001/11/19 10:20:34 martin Exp $ */
2
3 /*
4 * Copyright (c) 2001 Martin Husemann <martin (at) 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 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <sys/queue.h>
31 #include <net/if.h>
32 #include <net/if_dl.h>
33 #include <net/route.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <netdb.h>
42
43 /* local functions */
44 static void usage(void);
45 static void dispatch(void*, size_t);
46 static void check_addrs(char *cp, int addrs, int is_up);
47 static void invoke_script(struct sockaddr *sa, int is_up, int ifindex);
48 static void list_interfaces(const char *ifnames);
49 static void rescan_interfaces(void);
50 static void free_interfaces(void);
51 static int find_interface(int index);
52
53 /* stolen from /sbin/route */
54 #define ROUNDUP(a) \
55 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
56 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
57
58 /* global variables */
59 static int verbose = 0;
60 static const char *up_script = NULL;
61 static const char *down_script = NULL;
62
63 struct interface_data {
64 SLIST_ENTRY(interface_data) next;
65 int index;
66 char * ifname;
67 };
68 SLIST_HEAD(,interface_data) ifs = SLIST_HEAD_INITIALIZER(ifs);
69
70 int
71 main(int argc, char **argv)
72 {
73 int c, s, n;
74 int errs = 0;
75 char msg[2048], *msgp;
76
77 while ((c = getopt(argc, argv, "vhu:d:")) != -1)
78 switch (c) {
79 case 'h':
80 usage();
81 return 0;
82 case 'v':
83 verbose++;
84 break;
85
86 case 'u':
87 up_script = optarg;
88 break;
89
90 case 'd':
91 down_script = optarg;
92 break;
93
94 default:
95 errs++;
96 break;
97 }
98
99 if (errs)
100 usage();
101
102 argv += optind;
103 argc -= optind;
104
105 if (argc <= 0)
106 usage();
107
108 if (verbose) {
109 printf("up_script: %s\ndown_script: %s\n",
110 up_script, down_script);
111 printf("verbosity = %d\n", verbose);
112 }
113
114 while (argc > 0) {
115 list_interfaces(argv[0]);
116 argv++; argc--;
117 }
118
119 if (!verbose)
120 daemon(0,0);
121
122 s = socket(PF_ROUTE, SOCK_RAW, 0);
123 if (s < 0) {
124 perror("open routing socket");
125 exit(1);
126 }
127
128 for (;;) {
129 n = read(s, msg, sizeof msg);
130 msgp = msg;
131 for (msgp = msg; n > 0; n -= ((struct rt_msghdr*)msgp)->rtm_msglen, msgp += ((struct rt_msghdr*)msgp)->rtm_msglen) {
132 dispatch(msgp, n);
133
134 }
135 }
136
137 close(s);
138 free_interfaces();
139
140 exit(0);
141 }
142
143 static void
144 usage()
145 {
146 fprintf(stderr,
147 "usage:\n"
148 "\tifwatchd [-h] [-v] [-u up-script] [-d down-script] ifname(s)\n"
149 "\twhere:\n"
150 "\t -h show this help message\n"
151 "\t -v verbose/debug output, don't run in background\n"
152 "\t -u <cmd> specify command to run on interface up event\n"
153 "\t -d <cmd> specify command to run on interface down event\n");
154 exit(1);
155 }
156
157 static void
158 dispatch(void *msg, size_t len)
159 {
160 struct rt_msghdr *hd = msg;
161 struct ifa_msghdr *ifam;
162 int is_up;
163
164 is_up = 0;
165 switch (hd->rtm_type) {
166 case RTM_NEWADDR:
167 is_up = 1;
168 goto work;
169 case RTM_DELADDR:
170 is_up = 0;
171 goto work;
172 case RTM_IFANNOUNCE:
173 rescan_interfaces();
174 break;
175 }
176 if (verbose)
177 printf("unknown message ignored\n");
178 return;
179
180 work:
181 ifam = (struct ifa_msghdr *)msg;
182 check_addrs((char *)(ifam + 1), ifam->ifam_addrs, is_up);
183 }
184
185 static void
186 check_addrs(cp, addrs, is_up)
187 char *cp;
188 int addrs, is_up;
189 {
190 struct sockaddr *sa;
191 int ifndx = 0, i;
192
193 if (addrs == 0)
194 return;
195 for (i = 1; i; i <<= 1) {
196 if (i & addrs) {
197 sa = (struct sockaddr *)cp;
198 if (i == RTA_IFP) {
199 struct sockaddr_dl * li = (struct sockaddr_dl*)sa;
200 ifndx = li->sdl_index;
201 if (!find_interface(ifndx)) {
202 if (verbose)
203 printf("ignoring change on interface #%d\n", ifndx);
204 return;
205 }
206 } else if (i == RTA_IFA) {
207 invoke_script(sa, is_up, ifndx);
208 }
209 ADVANCE(cp, sa);
210 }
211 }
212 }
213
214 static void
215 invoke_script(sa, is_up, ifindex)
216 struct sockaddr *sa;
217 int is_up, ifindex;
218 {
219 char addr[NI_MAXHOST], ifname_buf[IFNAMSIZ], *ifname, *cmd;
220 const char *script;
221
222 ifname = if_indextoname(ifindex, ifname_buf);
223 if (sa->sa_len == 0) {
224 fprintf(stderr, "illegal socket address (sa_len == 0)\n");
225 return;
226 }
227
228 if (getnameinfo(sa, sa->sa_len, addr, sizeof addr, NULL, 0, NI_NUMERICHOST)) {
229 if (verbose)
230 printf("getnameinfo failed\n");
231 return; /* this address can not be handled */
232 }
233
234 script = is_up? up_script : down_script;
235 if (script == NULL) return;
236
237 asprintf(&cmd, "%s \"%s\" %s \"%s\"", script, ifname,
238 is_up?"up":"down", addr);
239 if (cmd == NULL) {
240 fprintf(stderr, "out of memory\n");
241 return;
242 }
243 if (verbose)
244 printf("calling: %s\n", cmd);
245 system(cmd);
246 free(cmd);
247 }
248
249 static void list_interfaces(const char *ifnames)
250 {
251 char * names = strdup(ifnames);
252 char * name, *lasts;
253 static const char sep[] = " \t";
254 struct interface_data * p;
255
256 for (name = strtok_r(names, sep, &lasts); name != NULL; name = strtok_r(NULL, sep, &lasts)) {
257 p = malloc(sizeof(*p));
258 SLIST_INSERT_HEAD(&ifs, p, next);
259 p->ifname = strdup(name);
260 p->index = if_nametoindex(p->ifname);
261 if (verbose)
262 printf("interface \"%s\" has index %d\n", p->ifname, p->index);
263 }
264 free(names);
265 }
266
267 static void rescan_interfaces()
268 {
269 struct interface_data * p;
270
271 SLIST_FOREACH(p, &ifs, next) {
272 p->index = if_nametoindex(p->ifname);
273 if (verbose)
274 printf("interface \"%s\" has index %d\n", p->ifname, p->index);
275 }
276 }
277
278 static void free_interfaces()
279 {
280 struct interface_data * p;
281
282 while (!SLIST_EMPTY(&ifs)) {
283 p = SLIST_FIRST(&ifs);
284 SLIST_REMOVE_HEAD(&ifs, next);
285 free(p->ifname);
286 free(p);
287 }
288 }
289
290 static int find_interface(index)
291 int index;
292 {
293 struct interface_data * p;
294
295 SLIST_FOREACH(p, &ifs, next)
296 if (p->index == index)
297 return 1;
298 return 0;
299 }
300