diff options
Diffstat (limited to 'src/daemon/priv-bsd.c')
-rw-r--r-- | src/daemon/priv-bsd.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/daemon/priv-bsd.c b/src/daemon/priv-bsd.c new file mode 100644 index 0000000..61f332a --- /dev/null +++ b/src/daemon/priv-bsd.c @@ -0,0 +1,202 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <unistd.h> +#include <net/bpf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +int +asroot_iface_init_os(int ifindex, char *name, int *fd) +{ + int enable, required, rc; + struct bpf_insn filter[] = { LLDPD_FILTER_F }; + struct ifreq ifr = { .ifr_name = {} }; + struct bpf_program fprog = { .bf_insns = filter, + .bf_len = sizeof(filter) / sizeof(struct bpf_insn) }; + +#ifndef HOST_OS_SOLARIS + int n = 0; + char dev[20]; + do { + snprintf(dev, sizeof(dev), "/dev/bpf%d", n++); + *fd = open(dev, O_RDWR); + } while (*fd < 0 && errno == EBUSY); +#else + *fd = open("/dev/bpf", O_RDWR); +#endif + if (*fd < 0) { + rc = errno; + log_warn("privsep", "unable to find a free BPF"); + return rc; + } + + /* Set buffer size */ + required = ETHER_MAX_LEN + BPF_WORDALIGN(sizeof(struct bpf_hdr)); + if (ioctl(*fd, BIOCSBLEN, (caddr_t)&required) < 0) { + rc = errno; + log_warn("privsep", "unable to set receive buffer size for BPF on %s", + name); + return rc; + } + + /* 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); + return rc; + } + + /* Disable buffering */ + enable = 1; + if (ioctl(*fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) { + rc = errno; + log_warn("privsep", "unable to disable buffering for %s", name); + return rc; + } + + /* 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); + return rc; + } + + /* Don't see sent packets */ +#ifdef HOST_OS_OPENBSD + enable = BPF_DIRECTION_OUT; + 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); + return rc; + } + + /* Install read filter */ + if (ioctl(*fd, BIOCSETF, (caddr_t)&fprog) < 0) { + rc = errno; + log_warn("privsep", "unable to setup BPF filter for %s", name); + return rc; + } +#ifdef BIOCSETWF + /* Install write filter (optional) */ + if (ioctl(*fd, BIOCSETWF, (caddr_t)&fprog) < 0) { + rc = errno; + log_info("privsep", "unable to setup write BPF filter for %s", name); + return rc; + } +#endif + +#ifdef BIOCLOCK + /* Lock interface, but first make it non blocking since we cannot do + * this later */ + levent_make_socket_nonblocking(*fd); + if (ioctl(*fd, BIOCLOCK, (caddr_t)&enable) < 0) { + rc = errno; + log_info("privsep", "unable to lock BPF interface %s", name); + return rc; + } +#endif + return 0; +} + +int +asroot_iface_description_os(const char *name, const char *description) +{ +#ifdef IFDESCRSIZE +# if defined HOST_OS_FREEBSD || defined HOST_OS_OPENBSD + char descr[IFDESCRSIZE]; + int rc, sock = -1; +# if defined HOST_OS_FREEBSD + struct ifreq ifr = { .ifr_buffer = { .buffer = descr, .length = IFDESCRSIZE } }; +# else + struct ifreq ifr = { .ifr_data = (caddr_t)descr }; +# endif + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == 1) { + rc = errno; + log_warnx("privsep", "unable to open inet socket"); + return rc; + } + if (strlen(description) == 0) { + /* No neighbor, try to append "was" to the current description */ + if (ioctl(sock, SIOCGIFDESCR, (caddr_t)&ifr) < 0) { + rc = errno; + log_warnx("privsep", "unable to get description of %s", name); + close(sock); + return rc; + } + if (strncmp(descr, "lldpd: ", 7) == 0) { + if (strncmp(descr + 7, "was ", 4) == 0) { + /* Already has an old neighbor */ + close(sock); + return 0; + } else { + /* Append was */ + memmove(descr + 11, descr + 7, sizeof(descr) - 11); + memcpy(descr, "lldpd: was ", 11); + } + } else { + /* No description, no neighbor */ + strlcpy(descr, "lldpd: no neighbor", sizeof(descr)); + } + } else + snprintf(descr, sizeof(descr), "lldpd: connected to %s", description); +# if defined HOST_OS_FREEBSD + ift.ifr_buffer.length = strlen(descr); +# endif + if (ioctl(sock, SIOCSIFDESCR, (caddr_t)&ifr) < 0) { + rc = errno; + log_warnx("privsep", "unable to set description of %s", name); + close(sock); + return rc; + } + close(sock); + return 0; +# endif +#endif /* IFDESCRSIZE */ + static int once = 0; + if (!once) { + log_warnx("privsep", "cannot set interface description for this OS"); + once = 1; + } + return 0; +} + +int +asroot_iface_promisc_os(const char *name) +{ + /* The promiscuous mode can be set when setting BPF + (BIOCPROMISC). Unfortunately, the interface is locked down and we + cannot change that without reopening a new socket. Let's do nothing + for now. */ + return 0; +} |