1 /* $NetBSD: ifwatchd.c,v 1.23 2008/05/24 17:45:14 joerg Exp $ */
4 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Martin Husemann <martin@NetBSD.org>.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Define this for special treatment of sys/net/if_spppsubr.c based interfaces.
35 #define SPPP_IF_SUPPORT
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41 #include <sys/queue.h>
44 #include <net/if_dl.h>
45 #include <net/if_media.h>
46 #ifdef SPPP_IF_SUPPORT
47 #include <net/if_sppp.h>
49 #include <net/route.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
63 enum event
{ ARRIVAL
, DEPARTURE
, UP
, DOWN
, CARRIER
, NO_CARRIER
};
66 static void usage(void);
67 static void dispatch(void*, size_t);
68 static void check_addrs(char *cp
, int addrs
, enum event ev
);
69 static void invoke_script(struct sockaddr
*sa
, struct sockaddr
*dst
, enum event ev
, int ifindex
, const char *ifname_hint
);
70 static void list_interfaces(const char *ifnames
);
71 static void check_announce(struct if_announcemsghdr
*ifan
);
72 static void check_carrier(int if_index
, int carrier
);
73 static void rescan_interfaces(void);
74 static void free_interfaces(void);
75 static int find_interface(int index
);
76 static void run_initial_ups(void);
78 #ifdef SPPP_IF_SUPPORT
79 static int check_is_connected(const char * ifname
, int def_retvalue
);
80 #define if_is_connected(X) (check_is_connected((X), 1))
81 #define if_is_not_connected(X) (!check_is_connected((X), 0))
83 #define if_is_connected(X) 1
84 #define if_is_not_connected(X) 1
87 /* stolen from /sbin/route */
89 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
90 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
92 /* global variables */
93 static int verbose
= 0, quiet
= 0;
94 static int inhibit_initial
= 0;
95 static const char *arrival_script
= NULL
;
96 static const char *departure_script
= NULL
;
97 static const char *up_script
= NULL
;
98 static const char *down_script
= NULL
;
99 static const char *carrier_script
= NULL
;
100 static const char *no_carrier_script
= NULL
;
101 static const char DummyTTY
[] = _PATH_DEVNULL
;
102 static const char DummySpeed
[] = "9600";
103 static const char **scripts
[] = {
112 struct interface_data
{
113 SLIST_ENTRY(interface_data
) next
;
115 int last_carrier_status
;
118 SLIST_HEAD(,interface_data
) ifs
= SLIST_HEAD_INITIALIZER(ifs
);
121 main(int argc
, char **argv
)
125 char msg
[2048], *msgp
;
127 openlog(argv
[0], LOG_PID
|LOG_CONS
, LOG_DAEMON
);
128 while ((c
= getopt(argc
, argv
, "qvhic:n:u:d:A:D:")) != -1) {
147 carrier_script
= optarg
;
151 no_carrier_script
= optarg
;
159 down_script
= optarg
;
163 arrival_script
= optarg
;
167 departure_script
= optarg
;
186 printf("up_script: %s\ndown_script: %s\n",
187 up_script
, down_script
);
188 printf("arrival_script: %s\ndeparture_script: %s\n",
189 arrival_script
, departure_script
);
190 printf("carrier_script: %s\nno_carrier_script: %s\n",
191 carrier_script
, no_carrier_script
);
192 printf("verbosity = %d\n", verbose
);
196 list_interfaces(argv
[0]);
204 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
206 syslog(LOG_ERR
, "error opening routing socket: %m");
207 perror("open routing socket");
211 if (!inhibit_initial
)
215 n
= read(s
, msg
, sizeof msg
);
217 for (msgp
= msg
; n
> 0;
218 n
-= ((struct rt_msghdr
*)msgp
)->rtm_msglen
,
219 msgp
+= ((struct rt_msghdr
*)msgp
)->rtm_msglen
)
235 "\tifwatchd [-hiqv] [-A arrival-script] [-D departure-script]\n"
236 "\t\t [-d down-script] [-u up-script]\n"
237 "\t\t [-c carrier-script] [-n no-carrier-script] ifname(s)\n"
239 "\t -A <cmd> specify command to run on interface arrival event\n"
240 "\t -c <cmd> specify command to run on interface carrier-detect event\n"
241 "\t -D <cmd> specify command to run on interface departure event\n"
242 "\t -d <cmd> specify command to run on interface down event\n"
243 "\t -n <cmd> specify command to run on interface no-carrier-detect event\n"
244 "\t -h show this help message\n"
245 "\t -i no (!) initial run of the up script if the interface\n"
246 "\t is already up on ifwatchd startup\n"
247 "\t -q quiet mode, don't syslog informational messages\n"
248 "\t -u <cmd> specify command to run on interface up event\n"
249 "\t -v verbose/debug output, don't run in background\n");
254 dispatch(void *msg
, size_t len
)
256 struct rt_msghdr
*hd
= msg
;
257 struct if_msghdr
*ifmp
;
258 struct ifa_msghdr
*ifam
;
261 switch (hd
->rtm_type
) {
270 check_announce((struct if_announcemsghdr
*)msg
);
273 ifmp
= (struct if_msghdr
*)msg
;
274 check_carrier(ifmp
->ifm_index
, ifmp
->ifm_data
.ifi_link_state
);
286 printf("unknown message ignored (%d)\n", hd
->rtm_type
);
290 ifam
= (struct ifa_msghdr
*)msg
;
291 check_addrs((char *)(ifam
+ 1), ifam
->ifam_addrs
, ev
);
295 check_addrs(char *cp
, int addrs
, enum event ev
)
297 struct sockaddr
*sa
, *ifa
= NULL
, *brd
= NULL
;
298 char ifname_buf
[IFNAMSIZ
];
304 for (i
= 1; i
; i
<<= 1) {
305 if ((i
& addrs
) == 0)
307 sa
= (struct sockaddr
*)cp
;
309 struct sockaddr_dl
* li
= (struct sockaddr_dl
*)sa
;
310 ifndx
= li
->sdl_index
;
311 if (!find_interface(ifndx
)) {
313 printf("ignoring change on interface #%d\n", ifndx
);
316 } else if (i
== RTA_IFA
)
318 else if (i
== RTA_BRD
)
323 ifname
= if_indextoname(ifndx
, ifname_buf
);
324 if (ifname
== NULL
|| ev
< UP
)
325 invoke_script(ifa
, brd
, ev
, ifndx
, ifname
);
327 if (if_is_connected(ifname
))
328 invoke_script(ifa
, brd
, ev
, ifndx
, ifname
);
329 } else if (ev
== DOWN
) {
330 if (if_is_not_connected(ifname
))
331 invoke_script(ifa
, brd
, ev
, ifndx
, ifname
);
337 invoke_script(struct sockaddr
*sa
, struct sockaddr
*dest
, enum event ev
,
338 int ifindex
, const char *ifname_hint
)
340 char addr
[NI_MAXHOST
], daddr
[NI_MAXHOST
], ifname_buf
[IFNAMSIZ
];
341 const char * volatile ifname
;
345 if (sa
!= NULL
&& sa
->sa_len
== 0) {
346 fprintf(stderr
, "illegal socket address (sa_len == 0)\n");
349 if (sa
!= NULL
&& sa
->sa_family
== AF_INET6
) {
350 struct sockaddr_in6 sin6
;
352 (void) memcpy(&sin6
, (struct sockaddr_in6
*)sa
, sizeof (sin6
));
353 if (IN6_IS_ADDR_LINKLOCAL(&sin6
.sin6_addr
))
357 addr
[0] = daddr
[0] = 0;
358 ifname
= if_indextoname(ifindex
, ifname_buf
);
359 ifname
= ifname
? ifname
: ifname_hint
;
364 if (getnameinfo(sa
, sa
->sa_len
, addr
, sizeof addr
, NULL
, 0,
367 printf("getnameinfo failed\n");
368 return; /* this address can not be handled */
372 if (getnameinfo(dest
, dest
->sa_len
, daddr
, sizeof daddr
,
373 NULL
, 0, NI_NUMERICHOST
)) {
375 printf("getnameinfo failed\n");
376 return; /* this address can not be handled */
380 script
= *scripts
[ev
];
381 if (script
== NULL
) return;
384 (void) printf("calling: %s %s %s %s %s %s\n",
385 script
, ifname
, DummyTTY
, DummySpeed
, addr
, daddr
);
387 syslog(LOG_INFO
, "calling: %s %s %s %s %s %s\n",
388 script
, ifname
, DummyTTY
, DummySpeed
, addr
, daddr
);
392 fprintf(stderr
, "cannot fork\n");
395 if (execl(script
, script
, ifname
, DummyTTY
, DummySpeed
,
396 addr
, daddr
, NULL
) == -1) {
397 syslog(LOG_ERR
, "could not execute \"%s\": %m",
403 (void) wait(&status
);
408 list_interfaces(const char *ifnames
)
410 char * names
= strdup(ifnames
);
412 static const char sep
[] = " \t";
413 struct interface_data
* p
;
415 for (name
= strtok_r(names
, sep
, &lasts
);
417 name
= strtok_r(NULL
, sep
, &lasts
)) {
418 p
= malloc(sizeof(*p
));
419 SLIST_INSERT_HEAD(&ifs
, p
, next
);
420 p
->last_carrier_status
= -1;
421 p
->ifname
= strdup(name
);
422 p
->index
= if_nametoindex(p
->ifname
);
424 syslog(LOG_INFO
, "watching interface %s", p
->ifname
);
426 printf("interface \"%s\" has index %d\n",
427 p
->ifname
, p
->index
);
433 check_carrier(int if_index
, int carrier_status
)
435 struct interface_data
* p
;
438 SLIST_FOREACH(p
, &ifs
, next
)
439 if (p
->index
== if_index
)
446 * Treat it as an event worth handling if:
447 * - the carrier status changed, or
448 * - this is the first time we've been called, and
449 * inhibit_initial is not set
452 if ((carrier_status
!= p
->last_carrier_status
) ||
453 ((p
->last_carrier_status
== -1) && !inhibit_initial
)) {
454 switch (carrier_status
) {
458 case LINK_STATE_DOWN
:
463 printf("unknown link status ignored\n");
466 invoke_script(NULL
, NULL
, ev
, if_index
, p
->ifname
);
467 p
->last_carrier_status
= carrier_status
;
472 check_announce(struct if_announcemsghdr
*ifan
)
474 struct interface_data
* p
;
475 const char *ifname
= ifan
->ifan_name
;
477 SLIST_FOREACH(p
, &ifs
, next
) {
478 if (strcmp(p
->ifname
, ifname
) != 0)
481 switch (ifan
->ifan_what
) {
483 invoke_script(NULL
, NULL
, ARRIVAL
, p
->index
,
487 invoke_script(NULL
, NULL
, DEPARTURE
, p
->index
,
492 (void) printf("unknown announce: "
493 "what=%d\n", ifan
->ifan_what
);
501 rescan_interfaces(void)
503 struct interface_data
* p
;
505 SLIST_FOREACH(p
, &ifs
, next
) {
506 p
->index
= if_nametoindex(p
->ifname
);
508 printf("interface \"%s\" has index %d\n", p
->ifname
,
514 free_interfaces(void)
516 struct interface_data
* p
;
518 while (!SLIST_EMPTY(&ifs
)) {
519 p
= SLIST_FIRST(&ifs
);
520 SLIST_REMOVE_HEAD(&ifs
, next
);
527 find_interface(int idx
)
529 struct interface_data
* p
;
531 SLIST_FOREACH(p
, &ifs
, next
)
538 run_initial_ups(void)
540 struct interface_data
* ifd
;
541 struct ifaddrs
*res
= NULL
, *p
;
544 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
548 if (getifaddrs(&res
) != 0)
551 for (p
= res
; p
; p
= p
->ifa_next
) {
552 SLIST_FOREACH(ifd
, &ifs
, next
) {
553 if (strcmp(ifd
->ifname
, p
->ifa_name
) == 0)
559 if (p
->ifa_addr
&& p
->ifa_addr
->sa_family
== AF_LINK
)
560 invoke_script(NULL
, NULL
, ARRIVAL
, ifd
->index
,
563 if ((p
->ifa_flags
& IFF_UP
) == 0)
565 if (p
->ifa_addr
== NULL
)
567 if (p
->ifa_addr
->sa_family
== AF_LINK
) {
568 struct ifmediareq ifmr
;
570 memset(&ifmr
, 0, sizeof(ifmr
));
571 strncpy(ifmr
.ifm_name
, ifd
->ifname
,
572 sizeof(ifmr
.ifm_name
));
573 if (ioctl(s
, SIOCGIFMEDIA
, &ifmr
) != -1
574 && (ifmr
.ifm_status
& IFM_AVALID
)
575 && (ifmr
.ifm_status
& IFM_ACTIVE
)) {
576 invoke_script(NULL
, NULL
, CARRIER
,
577 ifd
->index
, ifd
->ifname
);
578 ifd
->last_carrier_status
=
583 if (if_is_connected(ifd
->ifname
))
584 invoke_script(p
->ifa_addr
, p
->ifa_dstaddr
, UP
,
585 ifd
->index
, ifd
->ifname
);
592 #ifdef SPPP_IF_SUPPORT
594 * Special case support for in-kernel PPP interfaces.
595 * If these are IFF_UP, but have not yet connected or completed authentication
596 * we don't want to call the up script in the initial interface scan (there
597 * will be an UP event generated later, when IPCP completes, anyway).
599 * If this is no if_spppsubr.c based interface, this ioctl just fails and we
600 * treat is as connected.
603 check_is_connected(const char *ifname
, int def_retval
)
606 struct spppstatus oldstatus
;
607 struct spppstatusncp status
;
609 memset(&status
, 0, sizeof status
);
610 strncpy(status
.ifname
, ifname
, sizeof status
.ifname
);
611 memset(&oldstatus
, 0, sizeof oldstatus
);
612 strncpy(oldstatus
.ifname
, ifname
, sizeof oldstatus
.ifname
);
614 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
616 return 1; /* no idea how to handle this... */
617 error
= ioctl(s
, SPPPGETSTATUSNCP
, &status
);
619 error
= ioctl(s
, SPPPGETSTATUS
, &oldstatus
);
621 /* not if_spppsubr.c based - return default */
625 /* can't query NCPs, so use default */
626 status
.phase
= oldstatus
.phase
;
627 status
.ncpup
= def_retval
;
632 return status
.phase
== SPPP_PHASE_NETWORK
&& status
.ncpup
> 0;