/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * * 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 #include #include #include #include #include #include #include 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; }