From: Vincent Bernat Date: Mon, 14 Jan 2013 09:21:13 +0000 (+0100) Subject: priv: lock BPF interface on BSD X-Git-Tag: 0.7.2~52 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e735a3196e177977b539cce211cade3da4a30af7;p=thirdparty%2Flldpd.git priv: lock BPF interface on BSD 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. --- diff --git a/NEWS b/NEWS index f5fd1f21..4d291c1b 100644 --- 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 diff --git a/src/daemon/interfaces-bsd.c b/src/daemon/interfaces-bsd.c index ff09f169..02b1efbe 100644 --- a/src/daemon/interfaces-bsd.c +++ b/src/daemon/interfaces-bsd.c @@ -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); diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c index 021e41c7..3bfc2f0b 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -34,42 +33,17 @@ #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 >= diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c index facc69c8..2392b86a 100644 --- a/src/daemon/interfaces.c +++ b/src/daemon/interfaces.c @@ -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); diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 26e361a5..82163587 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -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 *); diff --git a/src/daemon/priv.c b/src/daemon/priv.c index f5ae82bb..d3335908 100644 --- a/src/daemon/priv.c +++ b/src/daemon/priv.c @@ -38,6 +38,13 @@ #include #ifdef HOST_OS_LINUX # include /* For sockaddr_ll */ +# include /* For BPF filtering */ +#endif +#if defined HOST_OS_FREEBSD || \ + HOST_OS_NETBSD || \ + HOST_OS_OPENBSD || \ + HOST_OS_OSX +# include #endif #if defined HOST_OS_FREEBSD || HOST_OS_OSX # include @@ -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