summaryrefslogtreecommitdiffstats
path: root/src/daemon/priv-bsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/priv-bsd.c')
-rw-r--r--src/daemon/priv-bsd.c202
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;
+}