From: Roy Marples Date: Mon, 9 Sep 2013 16:14:06 +0000 (+0000) Subject: Add udev support for interface arrival and departure. X-Git-Tag: v6.1.0~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=12bbc8cb5c7507be15a7e0af4140c3d81125c46c;p=thirdparty%2Fdhcpcd.git Add udev support for interface arrival and departure. This is because udev likes to rename the interface, which it can't do if dhcpcd grabs it first. --- diff --git a/configure b/configure index 69a5ad80..7b091f17 100755 --- a/configure +++ b/configure @@ -9,6 +9,7 @@ ARC4RANDOM= CLOSEFROM= GETLINE= STRLCPY= +UDEV= OS= BUILD= HOST= @@ -57,8 +58,9 @@ for x do --without-getline) GETLINE=no;; --without-strlcpy) STRLCPY=no;; --without-posix_spawn) POSIX_SPAWN=no;; - --without-pollts) POLLTS=xno;; + --without-pollts) POLLTS=no;; --with-pollts) POLLTS=$var;; + --without-udev) UDEV=no;; --serviceexists) SERVICEEXISTS=$var;; --servicecmd) SERVICECMD=$var;; --servicestatus) SERVICESTATUS=$var;; @@ -599,6 +601,42 @@ pselect) ;; esac +if [ "$UDEV" != no ]; then + printf "Checking for libudev ... " + LIBUDEV_CFLAGS=$(pkg-config --cflags libudev 2>/dev/null) + LIBUDEV_LIBS=$(pkg-config --libs libudev 2>/dev/null) +fi +if [ "$UDEV" != no -a -n "$LIBUDEV_LIBS" ]; then + echo "yes" + echo "SRCS+= dev/udev.c" >>$CONFIG_MK + if [ -n "$LIBUDEV_CFLAGS" ]; then + echo "CFLAGS+= $LIBUDEV_CFLAGS" >>$CONFIG_MK + fi + echo "CPPFLAGS+= -DLIBUDEV" >>$CONFIG_MK + echo "LDADD+= $LIBUDEV_LIBS" >>$CONFIG_MK + + printf "Checking udev_monitor_filter_add_match_subsystem_devtype ..." + cat <_udev.c +#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE +#include +#include +int main(void) { + udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL); + return 0; +} +EOF + if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS2 2>/dev/null + then + echo "CPPFLAGS+= -DLIBUDEV_FILTER" >>$CONFIG_MK + echo " yes" + else + echo " no" + fi + rm -f _udev.c _udev +elif [ "$UDEV" != no ]; then + echo "no" +fi + if [ -z "$SERVICECMD" ]; then printf "Checking for OpenRC ... " if [ -x /sbin/rc-service ]; then diff --git a/dev/udev.c b/dev/udev.c new file mode 100644 index 00000000..207253e8 --- /dev/null +++ b/dev/udev.c @@ -0,0 +1,133 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright (c) 2006-2013 Roy Marples + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE + +#include +#include +#include +#include + +#include "../common.h" +#include "../dhcpcd.h" +#include "../eloop.h" +#include "udev.h" + +static struct udev_monitor *monitor; + +static void +libudev_handledata(__unused void *arg) +{ + struct udev_device *device; + const char *ifname, *action; + + device = udev_monitor_receive_device(monitor); + if (device == NULL) { + syslog(LOG_DEBUG, "libudev: received NULL device"); + return; + } + + /* udev filter documentation says "usually" so double check */ + action = udev_device_get_subsystem(device); + if (strcmp(action, "net")) + return; + + ifname = udev_device_get_sysname(device); + action = udev_device_get_action(device); + if (strcmp(action, "add") == 0) + handle_interface(1, ifname); + else if (strcmp(action, "remove") == 0) + handle_interface(-1, ifname); +} + +int +libudev_listening(void) +{ + + return monitor == NULL ? 0 : 1; +} + +void +libudev_stop(void) +{ + struct udev *udev; + + if (monitor) { + udev = udev_monitor_get_udev(monitor); + udev_unref(udev); + udev_monitor_unref(monitor); + monitor = NULL; + } +} + +int +libudev_start(void) +{ + struct udev *udev; + int fd; + + syslog(LOG_DEBUG, "libudev: starting"); + udev = udev_new(); + if (udev == NULL) { + syslog(LOG_ERR, "udev_new: %m"); + return -1; + } + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (monitor == NULL) { + syslog(LOG_ERR, "udev_monitor_new_from_netlink: %m"); + goto bad; + } +#ifdef LIBUDEV_FILTER + if (udev_monitor_filter_add_match_subsystem_devtype(monitor, + "net", NULL) != 0) + { + syslog(LOG_ERR, + "udev_monitor_filter_add_match_subsystem_devtype: %m"); + goto bad; + } +#endif + if (udev_monitor_enable_receiving(monitor) != 0) { + syslog(LOG_ERR, "udev_monitor_enable_receiving: %m"); + goto bad; + } + fd = udev_monitor_get_fd(monitor); + if (fd == -1) { + syslog(LOG_ERR, "udev_monitor_get_fd: %m"); + goto bad; + } + if (eloop_event_add(fd, libudev_handledata, NULL) == -1) { + syslog(LOG_ERR, "%s: eloop_event_add: %m", __func__); + goto bad; + } + + atexit(libudev_stop); + + return fd; +bad: + + libudev_stop(); + return -1; +} diff --git a/dev/udev.h b/dev/udev.h new file mode 100644 index 00000000..a1702ffd --- /dev/null +++ b/dev/udev.h @@ -0,0 +1,34 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright (c) 2006-2013 Roy Marples + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef LIBUDEV_H +#define LIBUDEV_H + +int libudev_listening(void); +int libudev_start(void); +void libudev_stop(void); + +#endif diff --git a/if-linux.c b/if-linux.c index 6c893e28..6758a25a 100644 --- a/if-linux.c +++ b/if-linux.c @@ -61,6 +61,9 @@ #include "ipv4.h" #include "ipv6.h" #include "net.h" +#ifdef LIBUDEV +#include "dev/udev.h" +#endif static int sock_fd; static struct sockaddr_nl sock_nl; @@ -122,6 +125,7 @@ _open_link_socket(struct sockaddr_nl *nl) if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1) return -1; set_cloexec(fd); + return fd; } @@ -141,6 +145,14 @@ open_link_socket(void) { struct sockaddr_nl snl; +#ifdef LIBUDEV + /* If we have libudev, we need to listen to it for interface + * arrivals and departures so we get the interface name udev wants + * to give it. + * If we fail to work, we fall back to listening to the kernel. */ + libudev_start(); +#endif + memset(&snl, 0, sizeof(snl)); snl.nl_groups = RTMGRP_LINK; @@ -411,6 +423,13 @@ link_netlink(struct nlmsghdr *nlm) struct ifinfomsg *ifi; char ifn[IF_NAMESIZE + 1]; +#ifdef LIBUDEV + if (nlm->nlmsg_type == RTM_NEWLINK || nlm->nlmsg_type == RTM_DELLINK) { + if (libudev_listening()) + return 1; + } +#endif + len = link_route(nlm); if (len != 0) return len;