summaryrefslogtreecommitdiffstats
path: root/src/daemon/interfaces-bsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/interfaces-bsd.c')
-rw-r--r--src/daemon/interfaces-bsd.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/src/daemon/interfaces-bsd.c b/src/daemon/interfaces-bsd.c
new file mode 100644
index 0000000..a8248fe
--- /dev/null
+++ b/src/daemon/interfaces-bsd.c
@@ -0,0 +1,641 @@
+/* -*- 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 <errno.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/bpf.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/if_dl.h>
+#if defined HOST_OS_FREEBSD
+# include <net/if_vlan_var.h>
+# include <net/if_bridgevar.h>
+# include <net/if_lagg.h>
+#elif defined HOST_OS_DRAGONFLY
+# include <net/vlan/if_vlan_var.h>
+# include <net/bridge/if_bridgevar.h>
+#elif defined HOST_OS_OPENBSD
+# include <net/if_vlan_var.h>
+# include <net/if_bridge.h>
+# include <net/if_trunk.h>
+#elif defined HOST_OS_NETBSD
+# include <net/if_vlanvar.h>
+# include <net/if_bridgevar.h>
+# include <net/agr/if_agrioctl.h>
+#elif defined HOST_OS_OSX
+# include <osx/if_vlan_var.h>
+# include <osx/if_bridgevar.h>
+# include <osx/if_bond_var.h>
+#endif
+
+#ifndef IFDESCRSIZE
+# define IFDESCRSIZE 64
+#endif
+
+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)
+{
+ static size_t ifbic_len = 64;
+ struct ifbreq *req = NULL;
+ struct ifbifconf bifc = {};
+
+retry_alloc:
+ if ((req = realloc(req, ifbic_len)) == NULL) {
+ log_warn("interfaces", "unable to allocate memory to query bridge %s",
+ master->name);
+ free(bifc.ifbic_req);
+ return;
+ }
+ bifc.ifbic_len = ifbic_len;
+ bifc.ifbic_req = req;
+
+#if defined HOST_OS_FREEBSD || defined HOST_OS_NETBSD || defined HOST_OS_OSX || \
+ defined HOST_OS_DRAGONFLY
+ 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;
+ }
+#elif defined HOST_OS_OPENBSD
+ strlcpy(bifc.ifbic_name, master->name, sizeof(bifc.ifbic_name));
+ if (ioctl(cfg->g_sock, SIOCBRDGIFS, (caddr_t)&bifc) < 0) {
+ log_debug("interfaces", "%s is not a bridge", master->name);
+ return;
+ }
+#else
+# error Unsupported OS
+#endif
+ if (bifc.ifbic_len >= ifbic_len) {
+ ifbic_len = bifc.ifbic_len + 1;
+ goto retry_alloc;
+ }
+ 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)
+{
+#if defined HOST_OS_OPENBSD
+/* OpenBSD is the same as FreeBSD, just lagg->trunk */
+# define lagg_reqport trunk_reqport
+# define lagg_reqall trunk_reqall
+# define SIOCGLAGG SIOCGTRUNK
+# define LAGG_MAX_PORTS TRUNK_MAX_PORTS
+#endif
+#if defined HOST_OS_OPENBSD || defined HOST_OS_FREEBSD
+ 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;
+#elif defined HOST_OS_NETBSD
+ /* No max, we consider a maximum of 24 ports */
+ char buf[sizeof(struct agrportinfo) * 24] = {};
+ size_t buflen = sizeof(buf);
+ struct agrreq ar = { .ar_version = AGRREQ_VERSION,
+ .ar_cmd = AGRCMD_PORTLIST,
+ .ar_buf = buf,
+ .ar_buflen = buflen };
+ struct ifreq ifr = { .ifr_data = &ar };
+ struct agrportlist *apl = (void *)buf;
+ struct agrportinfo *api = (void *)(apl + 1);
+ strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
+ if (ioctl(cfg->g_sock, SIOCGETAGR, &ifr) == -1) {
+ if (errno == E2BIG) {
+ log_warnx("interfaces",
+ "%s is a too big aggregate. Please, report the problem",
+ master->name);
+ } else {
+ log_debug("interfaces", "%s is not an aggregate", master->name);
+ }
+ return;
+ }
+ for (int i = 0; i < apl->apl_nports; i++, api++) {
+ struct interfaces_device *slave;
+ slave = interfaces_nametointerface(interfaces, api->api_ifname);
+ if (slave == NULL) {
+ log_warnx("interfaces",
+ "%s should be enslaved to %s but we don't know %s",
+ api->api_ifname, master->name, api->api_ifname);
+ continue;
+ }
+ log_debug("interfaces", "%s is enslaved to bond %s", slave->name,
+ master->name);
+ slave->upper = master;
+ }
+ master->type |= IFACE_BOND_T;
+#elif defined HOST_OS_OSX
+ struct if_bond_req ibr = { .ibr_op = IF_BOND_OP_GET_STATUS,
+ .ibr_ibru = {
+ .ibru_status = { .ibsr_version = IF_BOND_STATUS_REQ_VERSION } } };
+ struct ifreq ifr = { .ifr_data = (caddr_t)&ibr };
+ strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
+ if (ioctl(cfg->g_sock, SIOCGIFBOND, (caddr_t)&ifr) < 0) {
+ log_debug("interfaces", "%s is not an aggregate", master->name);
+ return;
+ }
+ master->type |= IFACE_BOND_T;
+ if (ibr.ibr_ibru.ibru_status.ibsr_total == 0) {
+ log_debug("interfaces", "no members for bond %s", master->name);
+ return;
+ }
+
+ struct if_bond_status_req *ibsr_p = &ibr.ibr_ibru.ibru_status;
+ ibsr_p->ibsr_buffer =
+ malloc(sizeof(struct if_bond_status) * ibsr_p->ibsr_total);
+ if (ibsr_p->ibsr_buffer == NULL) {
+ log_warnx("interfaces", "not enough memory to check bond members");
+ return;
+ }
+ ibsr_p->ibsr_count = ibsr_p->ibsr_total;
+ if (ioctl(cfg->g_sock, SIOCGIFBOND, (caddr_t)&ifr) < 0) {
+ log_warn("interfaces", "unable to get members for bond %s",
+ master->name);
+ goto end;
+ }
+
+ struct if_bond_status *ibs_p = (struct if_bond_status *)ibsr_p->ibsr_buffer;
+ for (int i = 0; i < ibsr_p->ibsr_total; i++, ibs_p++) {
+ struct interfaces_device *slave;
+ slave = interfaces_nametointerface(interfaces, ibs_p->ibs_if_name);
+ if (slave == NULL) {
+ log_warnx("interfaces",
+ "%s should be enslaved to %s but we don't know %s",
+ ibs_p->ibs_if_name, master->name, ibs_p->ibs_if_name);
+ continue;
+ }
+ log_debug("interfaces", "%s is enslaved to bond %s", slave->name,
+ master->name);
+ slave->upper = master;
+ }
+end:
+ free(ibsr_p->ibsr_buffer);
+#elif defined HOST_OS_DRAGONFLY
+ log_debug("interfaces", "DragonFly BSD does not support link aggregation");
+#else
+# error Unsupported OS
+#endif
+}
+
+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;
+ bitmap_set(vlan->vlan_bmap, 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;
+}
+
+/* Remove any dangerous interface. Currently, only p2p0 is removed as it
+ * triggers some AirDrop functionality when we send something on it.
+ * See: https://github.com/lldpd/lldpd/issues/61
+ */
+static void
+ifbsd_denylist(struct lldpd *cfg, struct interfaces_device_list *interfaces)
+{
+#ifdef HOST_OS_OSX
+ struct interfaces_device *iface = NULL;
+ TAILQ_FOREACH (iface, interfaces, next) {
+ int i;
+ if (strncmp(iface->name, "p2p", 3)) continue;
+ if (strlen(iface->name) < 4) continue;
+ for (i = 3;
+ iface->name[i] != '\0' && isdigit((unsigned char)(iface->name[i]));
+ i++)
+ ;
+ if (iface->name[i] == '\0') {
+ log_debug("interfaces", "skip %s: AirDrop interface",
+ iface->name);
+ iface->ignore = 1;
+ }
+ }
+#endif
+}
+
+static struct interfaces_device *
+ifbsd_extract_device(struct lldpd *cfg, struct ifaddrs *ifaddr)
+{
+ struct interfaces_device *iface = NULL;
+ struct sockaddr_dl *saddrdl =
+ ALIGNED_CAST(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(ETHER_ADDR_LEN);
+ if (iface->address) memcpy(iface->address, LLADDR(saddrdl), ETHER_ADDR_LEN);
+
+ /* Grab description */
+#ifdef SIOCGIFDESCR
+# if defined HOST_OS_FREEBSD || defined HOST_OS_OPENBSD
+ iface->alias = malloc(IFDESCRSIZE);
+ if (iface->alias) {
+# if defined HOST_OS_FREEBSD
+ struct ifreq ifr = { .ifr_buffer = { .buffer = iface->alias,
+ .length = IFDESCRSIZE } };
+# else
+ struct ifreq ifr = { .ifr_data = (caddr_t)iface->alias };
+# endif
+ 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;
+ }
+ }
+# endif
+#endif /* SIOCGIFDESCR */
+
+ if (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;
+ 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) {
+#if defined HOST_OS_OPENBSD
+ /* On OpenBSD, the interface can have IFF_RUNNING but be down.
+ */
+ struct if_data *ifdata;
+ ifdata = ifaddr->ifa_data;
+ if (!LINK_STATE_IS_UP(ifdata->ifi_link_state))
+ device->flags &= ~IFF_RUNNING;
+#endif
+ 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
+ struct ifmediareq ifmr = {};
+# ifdef HAVE_TYPEOF
+ typeof(ifmr.ifm_ulist[0]) media_list[32] = {};
+# else
+ int media_list[32] = {};
+# endif
+ ifmr.ifm_ulist = media_list;
+ ifmr.ifm_count = 32;
+ 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 },
+# ifdef IFM_10G_T
+ { IFM_10G_T, LLDP_DOT3_MAU_10GIGBASECX4, LLDP_DOT3_MAU_10GIGBASECX4 },
+# endif
+# ifdef IFM_10G_TWINAX
+ { IFM_10G_TWINAX, LLDP_DOT3_MAU_10GIGBASECX4,
+ LLDP_DOT3_MAU_10GIGBASECX4 },
+# endif
+# ifdef IFM_10G_TWINAX_LONG
+ { IFM_10G_TWINAX_LONG, LLDP_DOT3_MAU_10GIGBASECX4,
+ LLDP_DOT3_MAU_10GIGBASECX4 },
+# endif
+# ifdef IFM_10G_LRM
+ { IFM_10G_LRM, LLDP_DOT3_MAU_10GIGBASELR, LLDP_DOT3_MAU_10GIGBASELR },
+# endif
+# ifdef IFM_10G_SFP_CU
+ { IFM_10G_SFP_CU, LLDP_DOT3_MAU_10GIGBASECX4,
+ LLDP_DOT3_MAU_10GIGBASECX4 },
+# endif
+ { 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_debug("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;
+ }
+
+ int found = 0;
+ 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];
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ port->p_macphy.autoneg_advertised |=
+ LLDP_DOT3_LINK_AUTONEG_OTHER;
+ }
+
+ 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
+}
+
+extern struct lldpd_ops bpf_ops;
+void
+interfaces_update(struct lldpd *cfg)
+{
+ struct lldpd_hardware *hardware;
+ struct interfaces_device *iface;
+ struct interfaces_device_list *interfaces;
+ struct interfaces_address_list *addresses;
+ 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);
+ }
+
+ ifbsd_denylist(cfg, interfaces);
+ interfaces_helper_allowlist(cfg, interfaces);
+ interfaces_helper_physical(cfg, interfaces, &bpf_ops, ifbpf_phys_init);
+#ifdef ENABLE_DOT1
+ interfaces_helper_vlan(cfg, interfaces);
+#endif
+ interfaces_helper_mgmt(cfg, addresses, interfaces);
+ interfaces_helper_chassis(cfg, interfaces);
+
+ /* Mac/PHY */
+ TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
+ if (!hardware->h_flags) continue;
+ ifbsd_macphy(cfg, hardware);
+ interfaces_helper_promisc(cfg, hardware);
+ }
+
+ if (cfg->g_iface_event == NULL) {
+ int s;
+ log_debug("interfaces", "subscribe to route socket notifications");
+ if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ log_warn("interfaces", "unable to open route socket");
+ goto end;
+ }
+
+#ifdef ROUTE_MSGFILTER
+ unsigned int rtfilter;
+ rtfilter = ROUTE_FILTER(RTM_IFINFO);
+ if (setsockopt(s, PF_ROUTE, ROUTE_MSGFILTER, &rtfilter,
+ sizeof(rtfilter)) == -1)
+ log_warn("interfaces",
+ "unable to set filter for interface updates");
+#endif
+
+ if (levent_iface_subscribe(cfg, s) == -1) close(s);
+ }
+
+end:
+ interfaces_free_devices(interfaces);
+ interfaces_free_addresses(addresses);
+ if (ifaddrs) freeifaddrs(ifaddrs);
+}
+
+void
+interfaces_cleanup(struct lldpd *cfg)
+{
+}