/* -*- 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 #if defined HOST_OS_FREEBSD # include # include # include #elif defined HOST_OS_DRAGONFLY # include # include #elif defined HOST_OS_OPENBSD # include # include # include #elif defined HOST_OS_NETBSD # include # include # include #elif defined HOST_OS_OSX # include # include # include #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) { }