]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
priv: lock BPF interface on BSD
authorVincent Bernat <vincent.bernat@dailymotion.com>
Mon, 14 Jan 2013 09:21:13 +0000 (10:21 +0100)
committerVincent Bernat <bernat@luffy.cx>
Mon, 14 Jan 2013 19:48:42 +0000 (20:48 +0100)
We move all BPF setup in the monitor process. When the socket is
configured, we lock it. This works for both OpenBSD and FreeBSD. This
feature does not seem to exist on Linux. However, for consistency, we
also move BPF filter setup in priv.c for Linux.

NEWS
src/daemon/interfaces-bsd.c
src/daemon/interfaces-linux.c
src/daemon/interfaces.c
src/daemon/lldpd.h
src/daemon/priv.c

diff --git a/NEWS b/NEWS
index f5fd1f212b95f5a8786fdc9ad6bfaeeaa24c3366..4d291c1b442ee4f704d29c41bf28917b3f468aa6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+lldpd (0.7.2)
+  * Features
+    + Lock BPF interfaces before handing them to chrooted process on
+      BSD.
+
 lldpd (0.7.1)
   * Features
     + Mac OS X support, sponsored by Xcloud, Mac cloud server hosting
index ff09f169654ae706c569ed47cc1d5169d916c4ee..02b1efbef83331e0b29d42690e517ef9ca8a8cd9 100644 (file)
@@ -584,91 +584,24 @@ static int
 ifbsd_phys_init(struct lldpd *cfg,
     struct lldpd_hardware *hardware)
 {
-       struct bpf_insn filter[] = { LLDPD_FILTER_F };
-       struct ifreq ifr = {};
-       struct bpf_program fprog = {
-               .bf_insns = filter,
-               .bf_len = sizeof(filter)/sizeof(struct bpf_insn)
-       };
        struct bpf_buffer *buffer = NULL;
-       int fd = -1, enable, required;
+       int fd = -1;
 
        log_debug("interfaces", "initialize ethernet device %s",
            hardware->h_ifname);
-       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
                return -1;
-       /* We got a file descriptor to /dev/bpfXXX */
 
-       /* Set buffer size */
-       required = ETHER_MAX_LEN;
-       if (ioctl(fd, BIOCSBLEN, (caddr_t)&required) < 0) {
-               log_warn("interfaces",
-                   "unable to set receive buffer size for BPF on %s",
-                   hardware->h_ifname);
-               goto end;
-       }
+       /* Allocate receive buffer */
        hardware->h_data = buffer =
-           malloc(required + sizeof(struct bpf_buffer));
+           malloc(ETHER_MAX_LEN + sizeof(struct bpf_buffer));
        if (buffer == NULL) {
                log_warn("interfaces",
                    "unable to allocate buffer space for BPF on %s",
                    hardware->h_ifname);
                goto end;
        }
-       buffer->len = required;
-
-       /* Bind the interface to BPF device */
-       strlcpy(ifr.ifr_name, hardware->h_ifname, IFNAMSIZ);
-       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
-               log_warn("interfaces", "failed to bind interface %s to BPF",
-                   hardware->h_ifname);
-               goto end;
-       }
-
-       /* Disable buffering */
-       enable = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) {
-               log_warn("interfaces", "unable to disable buffering for %s",
-                   hardware->h_ifname);
-               goto end;
-       }
-
-       /* Let us write the MAC address (raw packet mode) */
-       enable = 1;
-       if (ioctl(fd, BIOCSHDRCMPLT, (caddr_t)&enable) < 0) {
-               log_warn("interfaces",
-                   "unable to set the `header complete` flag for %s",
-                   hardware->h_ifname);
-               goto end;
-       }
-
-       /* Don't see sent packets */
-#ifdef HOST_OS_OPENBSD
-       enable = BPF_DIRECTION_IN;
-       if (ioctl(fd, BIOCSDIRFILT, (caddr_t)&enable) < 0) {
-#else
-       enable = 0;
-       if (ioctl(fd, BIOCSSEESENT, (caddr_t)&enable) < 0) {
-#endif
-               log_warn("interfaces",
-                   "unable to set packet direction for BPF filter on %s",
-                   hardware->h_ifname);
-               goto end;
-       }
-
-       /* Install read filter */
-       if (ioctl(fd, BIOCSETF, (caddr_t)&fprog) < 0) {
-               log_warn("interfaces", "unable to setup BPF filter for %s",
-                   hardware->h_ifname);
-               goto end;
-       }
-#ifdef BIOCSETWF
-       /* Install write filter (optional) */
-       if (ioctl(fd, BIOCSETWF, (caddr_t)&fprog) < 0) {
-               log_info("interfaces", "unable to setup write BPF filter for %s",
-                   hardware->h_ifname);
-       }
-#endif
+       buffer->len = ETHER_MAX_LEN;
 
        /* Setup multicast */
        interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
index 021e41c764c55aa807b5aacd4a9a3321a7424670..3bfc2f0b32cf29e1deba4c87a6d2e5868d991db0 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/if_bridge.h>
 #include <linux/wireless.h>
 #include <linux/sockios.h>
-#include <linux/filter.h>
 #include <linux/if_packet.h>
 #include <linux/ethtool.h>
 
 #define MAX_PORTS 1024
 #define MAX_BRIDGES 1024
 
-static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
-static int
-iflinux_set_filter(const char *name, int fd)
-{
-       struct sock_fprog prog;
-       log_debug("interfaces", "set BPF filter for %s", name);
-
-       memset(&prog, 0, sizeof(struct sock_fprog));
-       prog.filter = lldpd_filter_f;
-       prog.len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter);
-
-       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
-                &prog, sizeof(prog)) < 0) {
-               log_info("interfaces", "unable to change filter for %s", name);
-               return ENETDOWN;
-       }
-       return 0;
-}
-
 static int
 iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       int fd, status;
+       int fd;
 
        log_debug("interfaces", "initialize ethernet device %s",
            hardware->h_ifname);
-       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
                return -1;
        hardware->h_sendfd = fd; /* Send */
 
-       /* Set filter */
-       if ((status = iflinux_set_filter(hardware->h_ifname, fd)) != 0) {
-               close(fd);
-               return status;
-       }
-
        interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
 
        levent_hardware_add_fd(hardware, fd); /* Receive */
@@ -453,7 +427,7 @@ static int
 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
        struct bond_master *master = hardware->h_data;
-       int fd, status;
+       int fd;
        int un = 1;
 
        if (!master) return -1;
@@ -462,25 +436,17 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
            hardware->h_ifname);
 
        /* First, we get a socket to the raw physical interface */
-       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex,
+                       hardware->h_ifname)) == -1)
                return -1;
        hardware->h_sendfd = fd;
-       if ((status = iflinux_set_filter(hardware->h_ifname, fd)) != 0) {
-               close(fd);
-               return status;
-       }
        interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
 
        /* Then, we open a raw interface for the master */
-       if ((fd = priv_iface_init(master->index)) == -1) {
+       if ((fd = priv_iface_init(master->index, master->name)) == -1) {
                close(hardware->h_sendfd);
                return -1;
        }
-       if ((status = iflinux_set_filter(master->name, fd)) != 0) {
-               close(hardware->h_sendfd);
-               close(fd);
-               return status;
-       }
        /* With bonding and older kernels (< 2.6.27) we need to listen
         * to bond device. We use setsockopt() PACKET_ORIGDEV to get
         * physical device instead of bond device (works with >=
index facc69c8d57764d40689a81c6bd864298a12017d..2392b86aca87826b0327d00f72b208868335b57b 100644 (file)
@@ -519,7 +519,7 @@ interfaces_helper_physical(struct lldpd *cfg,
                                continue;
                        }
                        if (init(cfg, hardware) != 0) {
-                               log_warn("interfaces",
+                               log_warnx("interfaces",
                                    "unable to initialize %s",
                                    hardware->h_ifname);
                                lldpd_hardware_cleanup(cfg, hardware);
index 26e361a552272b96fad835b6d443190bd1bb997c..82163587b76e2ff3216367625594c4f64ab10e2e 100644 (file)
@@ -208,7 +208,7 @@ char        *priv_gethostbyname(void);
 int             priv_open(char*);
 int             priv_ethtool(char*, void*, size_t);
 #endif
-int             priv_iface_init(int);
+int             priv_iface_init(int, char *);
 int     priv_iface_multicast(const char *, u_int8_t *, int);
 int     priv_snmp_socket(struct sockaddr_un *);
 
index f5ae82bbe27757e6adc400fd9fa46ed4ae1f3fb5..d3335908324d54efacc141de38524e72cd34fc8b 100644 (file)
 #include <netdb.h>
 #ifdef HOST_OS_LINUX
 # include <netpacket/packet.h> /* For sockaddr_ll */
+# include <linux/filter.h>     /* For BPF filtering */
+#endif
+#if defined HOST_OS_FREEBSD || \
+           HOST_OS_NETBSD || \
+           HOST_OS_OPENBSD || \
+           HOST_OS_OSX
+# include <net/bpf.h>
 #endif
 #if defined HOST_OS_FREEBSD || HOST_OS_OSX
 # include <net/if_dl.h>
@@ -153,12 +160,15 @@ priv_ethtool(char *ifname, void *ethc, size_t length)
 #endif
 
 int
-priv_iface_init(int index)
+priv_iface_init(int index, char *iface)
 {
        int cmd, rc;
+       char dev[IFNAMSIZ];
        cmd = PRIV_IFACE_INIT;
        must_write(remote, &cmd, sizeof(int));
        must_write(remote, &index, sizeof(int));
+       strlcpy(dev, iface, IFNAMSIZ);
+       must_write(remote, dev, IFNAMSIZ);
        must_read(remote, &rc, sizeof(int));
        if (rc != 0) return -1;
        return receive_fd(remote);
@@ -324,43 +334,63 @@ asroot_ethtool()
 static void
 asroot_iface_init()
 {
-#if defined HOST_OS_LINUX
-       struct sockaddr_ll sa;
-       int s, rc = 0;
+       int rc = -1, fd = -1;
        int ifindex;
-
+       char name[IFNAMSIZ];
        must_read(remote, &ifindex, sizeof(ifindex));
+       must_read(remote, &name, sizeof(name));
+       name[sizeof(name) - 1] = '\0';
 
+#if defined HOST_OS_LINUX
        /* Open listening socket to receive/send frames */
-       if ((s = socket(PF_PACKET, SOCK_RAW,
+       if ((fd = socket(PF_PACKET, SOCK_RAW,
                    htons(ETH_P_ALL))) < 0) {
                rc = errno;
                must_write(remote, &rc, sizeof(rc));
                return;
        }
-       memset(&sa, 0, sizeof(sa));
-       sa.sll_family = AF_PACKET;
-       sa.sll_protocol = 0;
-       sa.sll_ifindex = ifindex;
-       if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
+
+       struct sockaddr_ll sa = {
+               .sll_family = AF_PACKET,
+               .sll_ifindex = ifindex
+       };
+       if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
                rc = errno;
-               must_write(remote, &rc, sizeof(rc));
-               close(s);
-               return;
+               log_warn("privsep",
+                   "unable to bind to raw socket for interface %s",
+                   name);
+               goto end;
        }
-       must_write(remote, &rc, sizeof(rc));
-       send_fd(remote, s);
-       close(s);
+
+       /* Set filter */
+       log_debug("privsep", "set BPF filter for %s", name);
+       static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
+       struct sock_fprog prog = {
+               .filter = lldpd_filter_f,
+               .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
+       };
+       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
+                &prog, sizeof(prog)) < 0) {
+               rc = errno;
+               log_info("privsep", "unable to change filter for %s", name);
+               goto end;
+       }
+
+       rc = 0;
+
 #elif defined HOST_OS_FREEBSD || \
       defined HOST_OS_OPENBSD || \
       defined HOST_OS_NETBSD  || \
       defined HOST_OS_OSX
-       int fd = -1, rc = 0, n = 0;
+       int n = 0;
+       int enable, required;
        char dev[20];
-       int ifindex;
-
-       must_read(remote, &ifindex, sizeof(ifindex));
-       /* Don't use ifindex */
+       struct bpf_insn filter[] = { LLDPD_FILTER_F };
+       struct ifreq ifr = {};
+       struct bpf_program fprog = {
+               .bf_insns = filter,
+               .bf_len = sizeof(filter)/sizeof(struct bpf_insn)
+       };
 
        do {
                snprintf(dev, sizeof(dev), "/dev/bpf%d", n++);
@@ -369,17 +399,98 @@ asroot_iface_init()
        if (fd < 0) {
                rc = errno;
                log_warn("privsep", "unable to find a free BPF");
-               must_write(remote, &rc, sizeof(rc));
-               return;
+               goto end;
        }
 
+       /* Set buffer size */
+       required = ETHER_MAX_LEN;
+       if (ioctl(fd, BIOCSBLEN, (caddr_t)&required) < 0) {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set receive buffer size for BPF on %s",
+                   name);
+               goto end;
+       }
+
+       /* Bind the interface to BPF device */
+       strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+               rc = errno;
+               log_warn("privsep", "failed to bind interface %s to BPF",
+                   name);
+               goto end;
+       }
+
+       /* Disable buffering */
+       enable = 1;
+       if (ioctl(fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to disable buffering for %s",
+                   name);
+               goto end;
+       }
+
+       /* Let us write the MAC address (raw packet mode) */
+       enable = 1;
+       if (ioctl(fd, BIOCSHDRCMPLT, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set the `header complete` flag for %s",
+                   name);
+               goto end;
+       }
+
+       /* Don't see sent packets */
+#ifdef HOST_OS_OPENBSD
+       enable = BPF_DIRECTION_IN;
+       if (ioctl(fd, BIOCSDIRFILT, (caddr_t)&enable) < 0)
+#else
+       enable = 0;
+       if (ioctl(fd, BIOCSSEESENT, (caddr_t)&enable) < 0)
+#endif
+       {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set packet direction for BPF filter on %s",
+                   name);
+               goto end;
+       }
+
+       /* Install read filter */
+       if (ioctl(fd, BIOCSETF, (caddr_t)&fprog) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to setup BPF filter for %s",
+                   name);
+               goto end;
+       }
+#ifdef BIOCSETWF
+       /* Install write filter (optional) */
+       if (ioctl(fd, BIOCSETWF, (caddr_t)&fprog) < 0) {
+               log_info("privsep", "unable to setup write BPF filter for %s",
+                   name);
+       }
+#endif
+
+#ifdef BIOCLOCK
+       /* Lock interface */
+       if (ioctl(fd, BIOCLOCK, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_info("privsep", "unable to lock BPF interface %s",
+                   name);
+               goto end;
+       }
+#endif
+
        rc = 0;
-       must_write(remote, &rc, sizeof(rc));
-       send_fd(remote, fd);
-       close(fd);
+
 #else
 #error Unsupported OS
 #endif
+
+end:
+       must_write(remote, &rc, sizeof(rc));
+       if (rc == 0 && fd >=0) send_fd(remote, fd);
+       if (fd >= 0) close(fd);
 }
 
 static void