From fda729fd1232135f1c4d9ef9f8fdace8be8600e3 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 25 Dec 2012 19:43:48 +0100 Subject: [PATCH] Add support for FreeBSD. lldpd is now able to run on FreeBSD (at least Debian GNU/kFreeBSD). Adaptation to other BSD should be easy. The support is complete and include VLAN, bridges and link aggregation. --- NEWS | 9 +- configure.ac | 2 +- m4/os.m4 | 2 + src/daemon/Makefile.am | 3 + src/daemon/interfaces-freebsd.c | 673 ++++++++++++++++++++++++++++++++ src/daemon/interfaces-linux.c | 10 +- src/daemon/interfaces.c | 3 +- src/daemon/lldp.c | 1 - src/daemon/priv.c | 60 ++- src/lldp-const.h | 2 +- 10 files changed, 744 insertions(+), 21 deletions(-) create mode 100644 src/daemon/interfaces-freebsd.c diff --git a/NEWS b/NEWS index dc6ccd38..cf88c852 100644 --- a/NEWS +++ b/NEWS @@ -1,13 +1,14 @@ lldpd (0.6.2) * Features: + + FreeBSD support. + Allow to disable LLDP protocol (with `-ll`). In this case, the first enabled protocol will be used when no neighbor is detected. + Allow to filter debug logs using tokens. Add more debug logs. + lldpctl can now output JSON. - + Use netlink to gather interface information. - + Don't use ioctl for bridges anymore. The configure option - `--enable-oldies` allow to reenable their uses for systems not - supporting sysfs. + + Use netlink to gather interface information on Linux. + + Don't use ioctl for bridges anymore on Linux. The configure + option `--enable-oldies` allow to reenable their uses for + systems not supporting sysfs. lldpd (0.6.1) * Features: diff --git a/configure.ac b/configure.ac index e969a4a6..9b47ae32 100644 --- a/configure.ac +++ b/configure.ac @@ -150,7 +150,7 @@ lldp_ARG_ENABLE([dot1], [Dot1 extension (VLAN stuff)], [yes]) lldp_ARG_ENABLE([dot3], [Dot3 extension (PHY stuff)], [yes]) # Oldies -lldp_ARG_ENABLE([oldies], [Compatibility with kernel older than 2.6.18], [no]) +lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.18], [no]) ####################### # Output results diff --git a/m4/os.m4 b/m4/os.m4 index d40027b0..e7d2fd25 100644 --- a/m4/os.m4 +++ b/m4/os.m4 @@ -18,6 +18,8 @@ AC_DEFUN([lldp_CHECK_OS], [ AC_MSG_CHECKING([if host OS is supported]) lldp_DEFINE_OS(linux*, Linux, LINUX) + lldp_DEFINE_OS(freebsd*, FreeBSD, FREEBSD) + lldp_DEFINE_OS(kfreebsd*, kFreeBSD, FREEBSD) if test x$os = x; then AC_MSG_RESULT(no) diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index d5eb5055..b8385014 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -27,6 +27,9 @@ liblldpd_la_SOURCES += \ interfaces-linux.c \ netlink.c endif +if HOST_OS_FREEBSD +liblldpd_la_SOURCES += interfaces-freebsd.c +endif # Add SNMP support if needed if USE_SNMP diff --git a/src/daemon/interfaces-freebsd.c b/src/daemon/interfaces-freebsd.c new file mode 100644 index 00000000..46214e8c --- /dev/null +++ b/src/daemon/interfaces-freebsd.c @@ -0,0 +1,673 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2012 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 +#include +#include +#include +#include + +#ifndef IFDESCRSIZE +#define IFDESCRSIZE 64 +#endif + +static int +ifbsd_check_driver(struct lldpd *cfg, + struct ifaddrs *ifaddr, + struct interfaces_device *iface) +{ + int name[6]; + char dname[IFNAMSIZ+1] = {}; + size_t len = IFNAMSIZ; + + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = iface->index; + name[5] = IFDATA_DRIVERNAME; + + if (sysctl(name, 6, dname, &len, 0, 0) == -1 || len == 0) { + log_info("interfaces", "unable to get driver name for %s", + iface->name); + return -1; + } + iface->driver = strdup(dname); + return 0; +} + +static int +ifbsd_check_wireless(struct lldpd *cfg, + struct ifaddrs *ifaddr, + struct interfaces_device *iface) +{ + struct ifmediareq ifmr = {}; + strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name)); + if (ioctl(cfg->g_sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0 || + IFM_TYPE(ifmr.ifm_current) != IFM_IEEE80211) + return 0; /* Not wireless either */ + iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T; + return 0; +} + +static void +ifbsd_check_bridge(struct lldpd *cfg, + struct interfaces_device_list *interfaces, + struct interfaces_device *master) +{ + struct ifbreq req[64]; + struct ifbifconf bifc = { + .ifbic_len = sizeof(req), + .ifbic_req = req + }; + struct ifdrv ifd = { + .ifd_cmd = BRDGGIFS, + .ifd_len = sizeof(bifc), + .ifd_data = &bifc + }; + + strlcpy(ifd.ifd_name, master->name, sizeof(ifd.ifd_name)); + if (ioctl(cfg->g_sock, SIOCGDRVSPEC, (caddr_t)&ifd) < 0) { + log_debug("interfaces", + "%s is not a bridge", master->name); + return; + } + if (bifc.ifbic_len >= sizeof(req)) { + log_warnx("interfaces", + "%s is a bridge too big. Please, report the problem", + master->name); + return; + } + for (int i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + struct interfaces_device *slave = + interfaces_nametointerface(interfaces, + req[i].ifbr_ifsname); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be bridged to %s but we don't know %s", + req[i].ifbr_ifsname, master->name, req[i].ifbr_ifsname); + continue; + } + log_debug("interfaces", + "%s is bridged to %s", + slave->name, master->name); + slave->upper = master; + } + master->type |= IFACE_BRIDGE_T; +} + +static void +ifbsd_check_bond(struct lldpd *cfg, + struct interfaces_device_list *interfaces, + struct interfaces_device *master) +{ + struct lagg_reqport rpbuf[LAGG_MAX_PORTS]; + struct lagg_reqall ra = { + .ra_size = sizeof(rpbuf), + .ra_port = rpbuf + }; + strlcpy(ra.ra_ifname, master->name, IFNAMSIZ); + if (ioctl(cfg->g_sock, SIOCGLAGG, (caddr_t)&ra) < 0) { + log_debug("interfaces", + "%s is not a bond", master->name); + return; + } + + for (int i = 0; i < ra.ra_ports; i++) { + struct interfaces_device *slave; + slave = interfaces_nametointerface(interfaces, + rpbuf[i].rp_portname); + if (slave == NULL) { + log_warnx("interfaces", + "%s should be enslaved to %s but we don't know %s", + rpbuf[i].rp_portname, master->name, + rpbuf[i].rp_portname); + continue; + } + log_debug("interfaces", + "%s is enslaved to bond %s", + slave->name, master->name); + slave->upper = master; + } + master->type |= IFACE_BOND_T; +} + +static void +ifbsd_check_vlan(struct lldpd *cfg, + struct interfaces_device_list *interfaces, + struct interfaces_device *vlan) +{ + struct interfaces_device *lower; + struct vlanreq vreq = {}; + struct ifreq ifr = { + .ifr_data = (caddr_t)&vreq + }; + strlcpy(ifr.ifr_name, vlan->name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGETVLAN, (caddr_t)&ifr) < 0) { + log_debug("interfaces", + "%s is not a VLAN", vlan->name); + return; + } + if (strlen(vreq.vlr_parent) == 0) { + log_debug("interfaces", + "%s is a VLAN but has no lower interface", + vlan->name); + vlan->lower = NULL; + vlan->type |= IFACE_VLAN_T; + return; + } + lower = interfaces_nametointerface(interfaces, + vreq.vlr_parent); + if (lower == NULL) { + log_warnx("interfaces", + "%s should be a VLAN of %s but %s does not exist", + vlan->name, vreq.vlr_parent, vreq.vlr_parent); + return; + } + log_debug("interfaces", + "%s is VLAN %d of %s", + vlan->name, vreq.vlr_tag, lower->name); + vlan->lower = lower; + vlan->vlanid = vreq.vlr_tag; + vlan->type |= IFACE_VLAN_T; +} + +static void +ifbsd_check_physical(struct lldpd *cfg, + struct interfaces_device_list *interfaces, + struct interfaces_device *iface) +{ + if (iface->type & (IFACE_VLAN_T| + IFACE_BOND_T|IFACE_BRIDGE_T|IFACE_PHYSICAL_T)) + return; + + if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) { + log_debug("interfaces", "skip %s: not able to do multicast nor broadcast", + iface->name); + return; + } + log_debug("interfaces", + "%s is a physical interface", + iface->name); + iface->type |= IFACE_PHYSICAL_T; +} + +static struct interfaces_device* +ifbsd_extract_device(struct lldpd *cfg, + struct ifaddrs *ifaddr) +{ + struct interfaces_device *iface = NULL; + struct sockaddr_dl *saddrdl = (struct sockaddr_dl*)ifaddr->ifa_addr; + if ((saddrdl->sdl_type != IFT_BRIDGE) && + (saddrdl->sdl_type != IFT_L2VLAN) && + (saddrdl->sdl_type != IFT_ETHER)) { + log_debug("interfaces", "skip %s: not an ethernet device (%d)", + ifaddr->ifa_name, saddrdl->sdl_type); + return NULL; + } + if ((iface = calloc(1, sizeof(struct interfaces_device))) == NULL) { + log_warn("interfaces", "unable to allocate memory for %s", + ifaddr->ifa_name); + return NULL; + } + + iface->index = saddrdl->sdl_index; + iface->name = strdup(ifaddr->ifa_name); + iface->flags = ifaddr->ifa_flags; + + /* MAC address */ + iface->address = malloc(ETH_ALEN); + if (iface->address) + memcpy(iface->address, LLADDR(saddrdl), ETH_ALEN); + + /* Grab description */ + iface->alias = malloc(IFDESCRSIZE); + if (iface->alias) { + struct ifreq ifr = { + .ifr_buffer = { .buffer = iface->alias, + .length = IFDESCRSIZE } + }; + strlcpy(ifr.ifr_name, ifaddr->ifa_name, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGIFDESCR, (caddr_t)&ifr) < 0) { + free(iface->alias); + iface->alias = NULL; + } + } + + if (ifbsd_check_driver(cfg, ifaddr, iface) == -1 || + ifbsd_check_wireless(cfg, ifaddr, iface) == -1) { + interfaces_free_device(iface); + return NULL; + } + + return iface; +} + +static void +ifbsd_extract(struct lldpd *cfg, + struct interfaces_device_list *interfaces, + struct interfaces_address_list *addresses, + struct ifaddrs *ifaddr) +{ + struct interfaces_address *address = NULL; + struct interfaces_device *device = NULL; + if (!ifaddr->ifa_name) return; + if (!ifaddr->ifa_addr) return; + if (!(ifaddr->ifa_flags & IFF_UP)) { + log_debug("interfaces", + "skip %s: down", ifaddr->ifa_name); + return; + } + switch (ifaddr->ifa_addr->sa_family) { + case AF_LINK: + log_debug("interfaces", + "grabbing information on interface %s", + ifaddr->ifa_name); + device = ifbsd_extract_device(cfg, ifaddr); + if (device) + TAILQ_INSERT_TAIL(interfaces, device, next); + break; + case AF_INET: + case AF_INET6: + log_debug("interfaces", + "got an IP address on %s", + ifaddr->ifa_name); + address = malloc(sizeof(struct interfaces_address)); + if (address == NULL) { + log_warn("interfaces", + "not enough memory for a new IP address on %s", + ifaddr->ifa_name); + return; + } + address->flags = ifaddr->ifa_flags; + address->index = if_nametoindex(ifaddr->ifa_name); + memcpy(&address->address, + ifaddr->ifa_addr, + (ifaddr->ifa_addr->sa_family == AF_INET)? + sizeof(struct sockaddr_in): + sizeof(struct sockaddr_in6)); + TAILQ_INSERT_TAIL(addresses, address, next); + break; + default: + log_debug("interfaces", "unhandled family %d for interface %s", + ifaddr->ifa_addr->sa_family, + ifaddr->ifa_name); + } +} + +static void +ifbsd_macphy(struct lldpd *cfg, + struct lldpd_hardware *hardware) +{ +#ifdef ENABLE_DOT3 + int media_list[32] = {}; + struct ifmediareq ifmr = { + .ifm_ulist = media_list, + .ifm_count = sizeof(media_list) / sizeof(int) + }; + struct lldpd_port *port = &hardware->h_lport; + unsigned int duplex; + unsigned int media; + int advertised_ifmedia_to_rfc3636[][3] = { + {IFM_10_T, + LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD}, + {IFM_10_STP, + LLDP_DOT3_LINK_AUTONEG_10BASE_T, + LLDP_DOT3_LINK_AUTONEG_10BASET_FD}, + {IFM_100_TX, + LLDP_DOT3_LINK_AUTONEG_100BASE_TX, + LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD}, + {IFM_100_T4, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4, + LLDP_DOT3_LINK_AUTONEG_100BASE_T4}, + {IFM_100_T2, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2, + LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD}, + {IFM_1000_SX, + LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD}, + {IFM_1000_LX, + LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD}, + {IFM_1000_CX, + LLDP_DOT3_LINK_AUTONEG_1000BASE_X, + LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD}, + {IFM_1000_T, + LLDP_DOT3_LINK_AUTONEG_1000BASE_T, + LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD}, + {0, 0, 0} + }; + int current_ifmedia_to_rfc3636[][3] = { + {IFM_10_T, + LLDP_DOT3_MAU_10BASETHD, LLDP_DOT3_MAU_10BASETFD}, + {IFM_10_STP, + LLDP_DOT3_MAU_10BASETHD, LLDP_DOT3_MAU_10BASETFD}, + {IFM_10_2, + LLDP_DOT3_MAU_10BASE2, LLDP_DOT3_MAU_10BASE2}, + {IFM_10_5, + LLDP_DOT3_MAU_10BASE5, LLDP_DOT3_MAU_10BASE5}, + {IFM_100_TX, + LLDP_DOT3_MAU_100BASETXHD, LLDP_DOT3_MAU_100BASETXFD}, + {IFM_100_FX, + LLDP_DOT3_MAU_100BASEFXHD, LLDP_DOT3_MAU_100BASEFXFD}, + {IFM_100_T2, + LLDP_DOT3_MAU_100BASET2HD, LLDP_DOT3_MAU_100BASET2FD}, + {IFM_1000_SX, + LLDP_DOT3_MAU_1000BASESXHD, LLDP_DOT3_MAU_1000BASESXFD}, + {IFM_10_FL, + LLDP_DOT3_MAU_10BASEFLHD, LLDP_DOT3_MAU_10BASEFLFD }, + {IFM_1000_LX, + LLDP_DOT3_MAU_1000BASELXHD, LLDP_DOT3_MAU_1000BASELXFD}, + {IFM_1000_CX, + LLDP_DOT3_MAU_1000BASECXHD, LLDP_DOT3_MAU_1000BASECXFD}, + {IFM_1000_T, + LLDP_DOT3_MAU_1000BASETHD, LLDP_DOT3_MAU_1000BASETFD }, + {IFM_10G_LR, + LLDP_DOT3_MAU_10GIGBASELR, LLDP_DOT3_MAU_10GIGBASELR}, + {IFM_10G_SR, + LLDP_DOT3_MAU_10GIGBASESR, LLDP_DOT3_MAU_10GIGBASESR}, + {IFM_10G_CX4, + LLDP_DOT3_MAU_10GIGBASELX4, LLDP_DOT3_MAU_10GIGBASELX4}, + {0, 0, 0} + }; + + log_debug("interfaces", "get MAC/phy for %s", + hardware->h_ifname); + strlcpy(ifmr.ifm_name, hardware->h_ifname, sizeof(ifmr.ifm_name)); + if (ioctl(cfg->g_sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + log_warn("interfaces", + "unable to get media information from %s", + hardware->h_ifname); + return; + } + if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) { + log_warnx("interfaces", + "cannot get media information from %s: not an ethernet device", + hardware->h_ifname); + return; + } + if ((ifmr.ifm_status & IFM_ACTIVE) == 0) { + log_debug("interfaces", + "interface %s is now down, skip", + hardware->h_ifname); + return; + } + if (ifmr.ifm_count == 0) { + log_warnx("interfaces", "no media information available on %s", + hardware->h_ifname); + return; + } + port->p_macphy.autoneg_support = + port->p_macphy.autoneg_enabled = 0; + for (int m = 0; m < ifmr.ifm_count; m++) { + media = IFM_SUBTYPE(ifmr.ifm_ulist[m]); + duplex = !!(IFM_OPTIONS(ifmr.ifm_ulist[m]) & + IFM_FDX); + if (media == IFM_AUTO) { + port->p_macphy.autoneg_support = 1; + port->p_macphy.autoneg_enabled = + (IFM_SUBTYPE(ifmr.ifm_current) == IFM_AUTO); + continue; + } + + for (int j = 0; advertised_ifmedia_to_rfc3636[j][0]; j++) { + if (advertised_ifmedia_to_rfc3636[j][0] == media) { + port->p_macphy.autoneg_advertised |= + advertised_ifmedia_to_rfc3636[j][1 + duplex]; + break; + } + } + } + + port->p_macphy.mau_type = 0; + media = IFM_SUBTYPE(ifmr.ifm_active); + duplex = !!(IFM_OPTIONS(ifmr.ifm_active) & IFM_FDX); + for (int j = 0; current_ifmedia_to_rfc3636[j][0]; j++) { + if (current_ifmedia_to_rfc3636[j][0] == media) { + port->p_macphy.mau_type = + current_ifmedia_to_rfc3636[j][1 + duplex]; + break; + } + } +#endif +} + +struct bpf_buffer { + size_t len; /* Total length of the buffer */ + char data[0]; /* Data */ +}; + +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; + + log_debug("interfaces", "initialize ethernet device %s", + hardware->h_ifname); + if ((fd = priv_iface_init(hardware->h_ifindex)) == -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; + } + hardware->h_data = buffer = + malloc(required + 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; + } + + /* We only want to receive incoming packets */ + enable = BPF_D_IN; + if (ioctl(fd, BIOCSDIRECTION, (caddr_t)&enable) < 0) { + 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; + } + /* 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); + } + + /* Setup multicast */ + interfaces_setup_multicast(cfg, hardware->h_ifname, 0); + + hardware->h_sendfd = fd; /* Send */ + + levent_hardware_add_fd(hardware, fd); /* Receive */ + log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname, + fd); + return 0; + +end: + if (fd >= 0) close(fd); + free(buffer); + hardware->h_data = NULL; + return -1; +} + +/* Ethernet send/receive through BPF */ +static int +ifbsd_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, + char *buffer, size_t size) +{ + log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)", + hardware->h_ifname, hardware->h_sendfd); + return write(hardware->h_sendfd, + buffer, size); +} + +static int +ifbsd_eth_recv(struct lldpd *cfg, + struct lldpd_hardware *hardware, + int fd, char *buffer, size_t size) +{ + int n; + struct bpf_buffer *bpfbuf = hardware->h_data; + struct bpf_hdr *bh; + log_debug("interfaces", "receive PDU from ethernet device %s", + hardware->h_ifname); + + /* We assume we have only receive one packet (unbuffered mode). Dunno if + * this is correct. */ + if ((n = read(fd, bpfbuf->data, bpfbuf->len)) == -1) { + log_warn("interfaces", "error while receiving frame on %s", + hardware->h_ifname); + hardware->h_rx_discarded_cnt++; + return -1; + } + bh = (struct bpf_hdr*)bpfbuf->data; + if (bh->bh_caplen < size) + size = bh->bh_caplen; + memcpy(buffer, bpfbuf->data + bh->bh_hdrlen, size); + + return size; +} + +static int +ifbsd_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + log_debug("interfaces", "close ethernet device %s", + hardware->h_ifname); + interfaces_setup_multicast(cfg, hardware->h_ifname, 1); + return 0; +} + +static struct lldpd_ops eth_ops = { + .send = ifbsd_eth_send, + .recv = ifbsd_eth_recv, + .cleanup = ifbsd_eth_close, +}; + +void +interfaces_update(struct lldpd *cfg) +{ + struct lldpd_hardware *hardware; + struct interfaces_device *iface; + struct interfaces_device_list *interfaces = NULL; + struct interfaces_address_list *addresses = NULL; + struct ifaddrs *ifaddrs = NULL, *ifaddr; + + interfaces = malloc(sizeof(struct interfaces_device_list)); + addresses = malloc(sizeof(struct interfaces_address_list)); + if (interfaces == NULL || addresses == NULL) { + log_warnx("interfaces", "unable to allocate memory"); + goto end; + } + TAILQ_INIT(interfaces); + TAILQ_INIT(addresses); + if (getifaddrs(&ifaddrs) < 0) { + log_warnx("interfaces", "unable to get list of interfaces"); + goto end; + } + + for (ifaddr = ifaddrs; + ifaddr != NULL; + ifaddr = ifaddr->ifa_next) { + ifbsd_extract(cfg, interfaces, addresses, ifaddr); + } + /* Link interfaces together if needed */ + TAILQ_FOREACH(iface, interfaces, next) { + ifbsd_check_bridge(cfg, interfaces, iface); + ifbsd_check_bond(cfg, interfaces, iface); + ifbsd_check_vlan(cfg, interfaces, iface); + ifbsd_check_physical(cfg, interfaces, iface); + } + + interfaces_helper_whitelist(cfg, interfaces); + interfaces_helper_physical(cfg, interfaces, + ð_ops, ifbsd_phys_init); +#ifdef ENABLE_DOT1 + interfaces_helper_vlan(cfg, interfaces); +#endif + interfaces_helper_mgmt(cfg, addresses); + interfaces_helper_chassis(cfg, interfaces); + + /* Mac/PHY */ + TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { + if (!hardware->h_flags) continue; + ifbsd_macphy(cfg, hardware); + } + +end: + interfaces_free_devices(interfaces); + interfaces_free_addresses(addresses); + if (ifaddrs) freeifaddrs(ifaddrs); + return; + +} diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c index c4be843f..d143501d 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -297,7 +297,7 @@ iflinux_get_permanent_mac(struct lldpd *cfg, FILE *netbond; const char const *slaveif = "Slave Interface: "; const char const *hwaddr = "Permanent HW addr: "; - u_int8_t mac[ETHER_ADDR_LEN]; + u_int8_t mac[ETH_ALEN]; char path[SYSFS_PATH_MAX]; char line[100]; @@ -358,14 +358,14 @@ iflinux_get_permanent_mac(struct lldpd *cfg, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != - ETHER_ADDR_LEN) { + ETH_ALEN) { log_warn("interfaces", "unable to parse %s", line + strlen(hwaddr)); fclose(netbond); return; } memcpy(iface->address, mac, - ETHER_ADDR_LEN); + ETH_ALEN); fclose(netbond); return; } @@ -415,7 +415,7 @@ iflinux_macphy(struct lldpd_hardware *hardware) if (ethc.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2; if (ethc.port == PORT_FIBRE) port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD; + LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD; break; case SPEED_100: port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \ @@ -630,7 +630,7 @@ iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces iface->flags = 0; /* Get local address */ - memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); + memcpy(&hardware->h_lladdr, iface->address, ETH_ALEN); /* Fill information about port */ interfaces_helper_port_name_desc(hardware, iface); diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c index e3908efd..5142ca5f 100644 --- a/src/daemon/interfaces.c +++ b/src/daemon/interfaces.c @@ -22,7 +22,6 @@ #include #include #include -#include #include /* Generic ethernet interface initialization */ @@ -540,7 +539,7 @@ interfaces_helper_physical(struct lldpd *cfg, interface. */ /* Get local address */ - memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); + memcpy(&hardware->h_lladdr, iface->address, ETH_ALEN); /* Fill information about port */ interfaces_helper_port_name_desc(hardware, iface); diff --git a/src/daemon/lldp.c b/src/daemon/lldp.c index 7b7eddec..0c992d39 100644 --- a/src/daemon/lldp.c +++ b/src/daemon/lldp.c @@ -25,7 +25,6 @@ #include #include #include -#include inline static int lldpd_af_to_lldp_proto(int af) diff --git a/src/daemon/priv.c b/src/daemon/priv.c index 3c62ce6e..e9ab45d0 100644 --- a/src/daemon/priv.c +++ b/src/daemon/priv.c @@ -35,7 +35,12 @@ #include #include #include -#include +#ifdef HOST_OS_LINUX +# include /* For sockaddr_ll */ +#endif +#ifdef HOST_OS_FREEBSD +# include +#endif #include /* Use resolv.h */ @@ -312,6 +317,7 @@ asroot_ethtool() static void asroot_iface_init() { +#if defined HOST_OS_LINUX struct sockaddr_ll sa; int s, rc = 0; int ifindex; @@ -338,21 +344,59 @@ asroot_iface_init() must_write(remote, &rc, sizeof(rc)); send_fd(remote, s); close(s); +#elif defined HOST_OS_FREEBSD + int fd = -1, rc = 0, n = 0; + char dev[20]; + int ifindex; + + must_read(remote, &ifindex, sizeof(ifindex)); + /* Don't use ifindex */ + + do { + snprintf(dev, sizeof(dev), "/dev/bpf%d", n++); + fd = open(dev, O_RDWR); + } while (fd < 0 && errno == EBUSY); + if (fd < 0) { + rc = errno; + log_warn("privsep", "unable to find a free BPF"); + must_write(remote, &rc, sizeof(rc)); + return; + } + + rc = 0; + must_write(remote, &rc, sizeof(rc)); + send_fd(remote, fd); + close(fd); +#else +#error Unsupported OS +#endif } static void asroot_iface_multicast() { int add, rc = 0; - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); + struct ifreq ifr = {}; must_read(remote, ifr.ifr_name, IFNAMSIZ); - ifr.ifr_name[IFNAMSIZ-1] = '\0'; +#if defined HOST_OS_LINUX must_read(remote, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - must_read(remote, &add, sizeof(int)); +#elif defined HOST_OS_FREEBSD + /* Black magic from mtest.c */ + struct sockaddr_dl *dlp = (struct sockaddr_dl *)&ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + must_read(remote, LLADDR(&dlp), ETH_ALEN); +#else +#error Unsupported OS +#endif - if (ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI, - &ifr) < 0) + must_read(remote, &add, sizeof(int)); + if ((ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI, + &ifr) < 0) && (errno != EADDRINUSE)) rc = errno; must_write(remote, &rc, sizeof(rc)); @@ -405,7 +449,9 @@ static struct dispatch_actions actions[] = { {PRIV_DELETE_CTL_SOCKET, asroot_ctl_cleanup}, {PRIV_GET_HOSTNAME, asroot_gethostbyname}, {PRIV_OPEN, asroot_open}, +#ifdef HOST_OS_LINUX {PRIV_ETHTOOL, asroot_ethtool}, +#endif {PRIV_IFACE_INIT, asroot_iface_init}, {PRIV_IFACE_MULTICAST, asroot_iface_multicast}, {PRIV_SNMP_SOCKET, asroot_snmp_socket}, diff --git a/src/lldp-const.h b/src/lldp-const.h index 52e1499f..9a02546f 100644 --- a/src/lldp-const.h +++ b/src/lldp-const.h @@ -54,7 +54,7 @@ #define LLDP_DOT3_MAU_10BASETHD 10 #define LLDP_DOT3_MAU_10BASETFD 11 #define LLDP_DOT3_MAU_10BASEFLHD 12 -#define LLDP_DOT3_MAU_10BASEFLDF 13 +#define LLDP_DOT3_MAU_10BASEFLFD 13 #define LLDP_DOT3_MAU_10BASET4 14 #define LLDP_DOT3_MAU_100BASETXHD 15 #define LLDP_DOT3_MAU_100BASETXFD 16 -- 2.39.5