]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add support for FreeBSD.
authorVincent Bernat <bernat@luffy.cx>
Tue, 25 Dec 2012 18:43:48 +0000 (19:43 +0100)
committerVincent Bernat <bernat@luffy.cx>
Wed, 26 Dec 2012 00:11:52 +0000 (01:11 +0100)
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
configure.ac
m4/os.m4
src/daemon/Makefile.am
src/daemon/interfaces-freebsd.c [new file with mode: 0644]
src/daemon/interfaces-linux.c
src/daemon/interfaces.c
src/daemon/lldp.c
src/daemon/priv.c
src/lldp-const.h

diff --git a/NEWS b/NEWS
index dc6ccd38f3ecae15256bbba9e05846440e7424ec..cf88c852293bf3c7044b246ed4c5dc76d6fb11e8 100644 (file)
--- 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:
index e969a4a692b5ed5aac3e8ec32ffaa6ac6289fee7..9b47ae32f3ac63f3cb6e8cc9d57a39a6371d43ad 100644 (file)
@@ -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
index d40027b05140ff26b78c78439ded17ff38a433bb..e7d2fd254b291e4c3b0005a144196719470b2f8c 100644 (file)
--- 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)
index d5eb5055283e49a2793782f7e264e2a7baa8810c..b838501495a01f01f156d203b9eb647e7f5c8afe 100644 (file)
@@ -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 (file)
index 0000000..46214e8
--- /dev/null
@@ -0,0 +1,673 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 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 <ifaddrs.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/bpf.h>
+#include <net/if_types.h>
+#include <net/if_mib.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+#include <net/if_bridgevar.h>
+#include <net/if_lagg.h>
+#include <net/if_dl.h>
+
+#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,
+           &eth_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;
+
+}
index c4be843f6d74a4ef4a741b52f36e9a79e259668b..d143501d752ab5bea5d70598a06b9e6306277eec 100644 (file)
@@ -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);
index e3908efd11b23df5f71e5a12c14f45213cf1ef66..5142ca5fe56af684ef3e86d79acbd5e3adcde34e 100644 (file)
@@ -22,7 +22,6 @@
 #include <errno.h>
 #include <assert.h>
 #include <fnmatch.h>
-#include <netpacket/packet.h>
 #include <arpa/inet.h>
 
 /* 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);
index 7b7eddec44ba06a1aeb4e976b8f7b9da5fed2441..0c992d391cca0a1cb2828a524be99231975b125a 100644 (file)
@@ -25,7 +25,6 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
-#include <netpacket/packet.h>
 
 inline static int
 lldpd_af_to_lldp_proto(int af)
index 3c62ce6e0128c2ab6e3e92ca126993b2739210a5..e9ab45d08bcee32abfafa4148dd7e8efe17351e1 100644 (file)
 #include <sys/utsname.h>
 #include <sys/ioctl.h>
 #include <netdb.h>
-#include <netpacket/packet.h>
+#ifdef HOST_OS_LINUX
+# include <netpacket/packet.h> /* For sockaddr_ll */
+#endif
+#ifdef HOST_OS_FREEBSD
+# include <net/if_dl.h>
+#endif
 #include <net/ethernet.h>
 
 /* 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},
index 52e1499f0a4435c85fec3fe9b2f692128150604c..9a02546fee504137550d3a7963cdef09dbf8d393 100644 (file)
@@ -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