From 6e75df87a63cc6c7c12149001487410a316d7f65 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 2 Jun 2009 09:56:25 +0200 Subject: [PATCH] Change the way interface information are collected. Move interface related stuff into interfaces.c. A set of handlers are called sequentially to handle interfaces. --- src/Makefile.am | 2 +- src/agent.c | 58 ++-- src/cdp.c | 3 +- src/dmi.c | 92 ++++++ src/edp.c | 5 +- src/features.c | 396 ------------------------- src/interfaces.c | 750 +++++++++++++++++++++++++++++++++++++++++++++++ src/lldp.c | 4 +- src/lldpd.c | 563 +++++++++-------------------------- src/lldpd.h | 55 ++-- src/priv.c | 32 +- src/sonmp.c | 8 +- 12 files changed, 1063 insertions(+), 905 deletions(-) create mode 100644 src/dmi.c delete mode 100644 src/features.c create mode 100644 src/interfaces.c diff --git a/src/Makefile.am b/src/Makefile.am index 2829b54f..0d267199 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ sbin_PROGRAMS = lldpd lldpctl COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h edp.h -lldpd_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c features.c client.c priv.c privsep_fdpass.c $(COMMON) +lldpd_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c interfaces.c client.c priv.c privsep_fdpass.c dmi.c $(COMMON) lldpctl_SOURCES = lldpctl.c $(COMMON) lldpd_LDADD = @LIBOBJS@ diff --git a/src/agent.c b/src/agent.c index bb9178dc..01b50a18 100644 --- a/src/agent.c +++ b/src/agent.c @@ -56,18 +56,16 @@ header_portindexed_table(struct variable *vp, oid *name, size_t *length, port = name[*length - 1]; distance = -1; TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) { - if (INTERFACE_OPENED(hardware)) { - aport = if_nametoindex(hardware->h_ifname); - if (aport == port) { - /* Exact match */ - return hardware; - } - if (aport < port) - continue; - if (aport - port < distance) { - phardware = hardware; - distance = aport - port; - } + aport = hardware->h_ifindex; + if (aport == port) { + /* Exact match */ + return hardware; + } + if (aport < port) + continue; + if (aport - port < distance) { + phardware = hardware; + distance = aport - port; } } if (phardware == NULL) @@ -119,7 +117,6 @@ header_tprindexed_table(struct variable *vp, oid *name, size_t *length, target = &name[vp->namelen]; target_len = *length - vp->namelen; TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) { - if (!INTERFACE_OPENED(hardware)) continue; TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { if ((variant == TPR_VARIANT_IP) && (port->p_chassis->c_mgmt.s_addr == INADDR_ANY)) @@ -129,7 +126,7 @@ header_tprindexed_table(struct variable *vp, oid *name, size_t *length, (port->p_lastchange - starttime.tv_sec)*100; else current[0] = 0; - current[1] = if_nametoindex(hardware->h_ifname); + current[1] = hardware->h_ifindex; current[2] = port->p_chassis->c_index; k = j = 0; switch (variant) { @@ -206,22 +203,20 @@ header_pvindexed_table(struct variable *vp, oid *name, size_t *length, target = &name[vp->namelen]; target_len = *length - vp->namelen; TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) { - if (INTERFACE_OPENED(hardware)) { - TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans, v_entries) { - current[0] = if_nametoindex(hardware->h_ifname); - current[1] = vlan->v_vid; - if ((result = snmp_oid_compare(current, 2, target, - target_len)) < 0) - continue; - if ((result == 0) && !exact) - continue; - if (result == 0) - return vlan; - if (snmp_oid_compare(current, 2, best, 2) < 0) { - memcpy(best, current, sizeof(oid) * 2); - pvlan = vlan; - } - } + TAILQ_FOREACH(vlan, &hardware->h_lport.p_vlans, v_entries) { + current[0] = hardware->h_ifindex; + current[1] = vlan->v_vid; + if ((result = snmp_oid_compare(current, 2, target, + target_len)) < 0) + continue; + if ((result == 0) && !exact) + continue; + if (result == 0) + return vlan; + if (snmp_oid_compare(current, 2, best, 2) < 0) { + memcpy(best, current, sizeof(oid) * 2); + pvlan = vlan; + } } } if (pvlan == NULL) @@ -259,7 +254,6 @@ header_tprvindexed_table(struct variable *vp, oid *name, size_t *length, target = &name[vp->namelen]; target_len = *length - vp->namelen; TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) { - if (!INTERFACE_OPENED(hardware)) continue; TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { if (port->p_lastchange > starttime.tv_sec) @@ -267,7 +261,7 @@ header_tprvindexed_table(struct variable *vp, oid *name, size_t *length, (port->p_lastchange - starttime.tv_sec)*100; else current[0] = 0; - current[1] = if_nametoindex(hardware->h_ifname); + current[1] = hardware->h_ifindex; current[2] = port->p_chassis->c_index; current[3] = vlan->v_vid; if ((result = snmp_oid_compare(current, 4, target, diff --git a/src/cdp.c b/src/cdp.c index 8d804a0d..e3c18ffa 100644 --- a/src/cdp.c +++ b/src/cdp.c @@ -165,7 +165,8 @@ cdp_send(struct lldpd *global, POKE_RESTORE(pos_checksum); if (!(POKE_UINT16(ntohs(checksum)))) goto toobig; - if (write(hardware->h_raw, packet, end - packet) == -1) { + if (hardware->h_ops->send(global, hardware, + (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", hardware->h_ifname); free(packet); diff --git a/src/dmi.c b/src/dmi.c new file mode 100644 index 00000000..ccef56de --- /dev/null +++ b/src/dmi.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009 Vincent Bernat + * + * Permission to use, copy, modify, and 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. + */ + +#define INCLUDE_LINUX_IF_H +#include "lldpd.h" +#include + +#ifdef ENABLE_LLDPMED + /* Fill in inventory stuff: + - hardware version: /sys/class/dmi/id/product_version + - firmware version: /sys/class/dmi/id/bios_version + - software version: `uname -r` + - serial number: /sys/class/dmi/id/product_serial + - manufacturer: /sys/class/dmi/id/sys_vendor + - model: /sys/class/dmi/id/product_name + - asset: /sys/class/dmi/id/chassis_asset_tag + */ + +char* +dmi_get(char *file) +{ + int dmi, s; + char buffer[100]; + + if ((dmi = priv_open(file)) < 0) { + LLOG_DEBUG("cannot get DMI file %s", file); + return NULL; + } + memset(buffer, 0, sizeof(buffer)); + if ((s = read(dmi, buffer, sizeof(buffer))) == -1) { + LLOG_DEBUG("cannot read DMI file %s", file); + close(dmi); + return NULL; + } + close(dmi); + buffer[sizeof(buffer) - 1] = '\0'; + if ((s > 0) && (buffer[s-1] == '\n')) + buffer[s-1] = '\0'; + if (strlen(buffer)) + return strdup(buffer); + return NULL; +} + +char* +dmi_hw() +{ + return dmi_get(SYSFS_CLASS_DMI "product_version"); +} + +char* +dmi_fw() +{ + return dmi_get(SYSFS_CLASS_DMI "bios_version"); +} + +char* +dmi_sn() +{ + return dmi_get(SYSFS_CLASS_DMI "product_serial"); +} + +char* +dmi_manuf() +{ + return dmi_get(SYSFS_CLASS_DMI "sys_vendor"); +} + +char* +dmi_model() +{ + return dmi_get(SYSFS_CLASS_DMI "product_name"); +} + +char* +dmi_asset() +{ + return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag"); +} +#endif diff --git a/src/edp.c b/src/edp.c index 7844f247..b9197ed3 100644 --- a/src/edp.c +++ b/src/edp.c @@ -135,7 +135,7 @@ edp_send(struct lldpd *global, if (deviceslot[i] == NULL) { if (!( POKE_UINT16(8) && - POKE_UINT16(if_nametoindex(hardware->h_ifname)))) + POKE_UINT16(hardware->h_ifindex))) goto toobig; } if (!( @@ -194,7 +194,8 @@ edp_send(struct lldpd *global, checksum = frame_checksum(pos_edp, v, 0); if (!(POKE_UINT16(ntohs(checksum)))) goto toobig; - if (write(hardware->h_raw, packet, end - packet) == -1) { + if (hardware->h_ops->send(global, hardware, + (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", hardware->h_ifname); free(packet); diff --git a/src/features.c b/src/features.c deleted file mode 100644 index fe988b55..00000000 --- a/src/features.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * Permission to use, copy, modify, and 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. - */ - -#define INCLUDE_LINUX_IF_H -#include "lldpd.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SYSFS_PATH_MAX 256 -#define MAX_PORTS 1024 -#define MAX_BRIDGES 1024 - -/* net/if.h */ -extern unsigned int if_nametoindex (__const char *__ifname) __THROW; -extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW; - -static int -old_iface_is_bridge(struct lldpd *cfg, const char *name) -{ - int ifindices[MAX_BRIDGES]; - char ifname[IFNAMSIZ]; - int num, i; - unsigned long args[3] = { BRCTL_GET_BRIDGES, - (unsigned long)ifindices, MAX_BRIDGES }; - if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 0) { - if (errno != ENOPKG) - LLOG_INFO("unable to get available bridges"); - return 0; - } - for (i = 0; i < num; i++) { - if (if_indextoname(ifindices[i], ifname) == NULL) - LLOG_INFO("unable to get name of interface %d", - ifindices[i]); - else if (strncmp(name, ifname, IFNAMSIZ) == 0) - return 1; - } - return 0; -} - -int -iface_is_bridge(struct lldpd *cfg, const char *name) -{ - char path[SYSFS_PATH_MAX]; - int f; - - if ((snprintf(path, SYSFS_PATH_MAX, - SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX) - LLOG_WARNX("path truncated"); - if ((f = priv_open(path)) < 0) { - return old_iface_is_bridge(cfg, name); - } - close(f); - return 1; -} - -static int -old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) -{ - int j, index = if_nametoindex(slave); - int ifptindices[MAX_PORTS]; - unsigned long args2[4] = { BRCTL_GET_PORT_LIST, - (unsigned long)ifptindices, MAX_PORTS, 0 }; - struct ifreq ifr; - - strncpy(ifr.ifr_name, master, IFNAMSIZ); - memset(ifptindices, 0, sizeof(ifptindices)); - ifr.ifr_data = (char *)&args2; - - if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0) { - LLOG_WARN("unable to get bridge members for %s", - ifr.ifr_name); - return 0; - } - - for (j = 0; j < MAX_PORTS; j++) { - if (ifptindices[j] == index) - return 1; - } - - return 0; -} - -int -iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) -{ - char path[SYSFS_PATH_MAX]; - int f; - - /* Master should be a bridge, first */ - if (!iface_is_bridge(cfg, master)) return 0; - - if (snprintf(path, SYSFS_PATH_MAX, - SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no", - master, slave) >= SYSFS_PATH_MAX) - LLOG_WARNX("path truncated"); - if ((f = priv_open(path)) < 0) { - return old_iface_is_bridged_to(cfg, slave, master); - } - close(f); - return 1; -} - -int -iface_is_vlan(struct lldpd *cfg, const char *name) -{ - struct vlan_ioctl_args ifv; - memset(&ifv, 0, sizeof(ifv)); - ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; - if ((strlcpy(ifv.device1, name, sizeof(ifv.device1))) >= - sizeof(ifv.device1)) - LLOG_WARNX("device name truncated"); - if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) - return 1; - return 0; -} - -int -iface_is_wireless(struct lldpd *cfg, const char *name) -{ - struct iwreq iwr; - strlcpy(iwr.ifr_name, name, IFNAMSIZ); - if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) - return 1; - return 0; -} - -int -iface_is_bond(struct lldpd *cfg, const char *name) -{ - struct ifreq ifr; - struct ifbond ifb; - memset(&ifr, 0, sizeof(ifr)); - memset(&ifb, 0, sizeof(ifb)); - strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); - ifr.ifr_data = &ifb; - if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) - return 1; - return 0; -} - -int -iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master, - int *active) -{ - struct ifreq ifr; - struct ifbond ifb; - struct ifslave ifs; - memset(&ifr, 0, sizeof(ifr)); - memset(&ifb, 0, sizeof(ifb)); - strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); - ifr.ifr_data = &ifb; - if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) { - while (ifb.num_slaves--) { - memset(&ifr, 0, sizeof(ifr)); - memset(&ifs, 0, sizeof(ifs)); - strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); - ifr.ifr_data = &ifs; - ifs.slave_id = ifb.num_slaves; - if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) && - (strncmp(ifs.slave_name, slave, sizeof(ifs.slave_name)) == 0)) { - if (active) - *active = ifs.state; - return 1; - } - } - } - return 0; -} - -int -iface_is_enslaved(struct lldpd *cfg, const char *name) -{ - struct ifaddrs *ifap, *ifa; - int master; - - if (getifaddrs(&ifap) != 0) { - LLOG_WARN("unable to get interface list"); - return -1; - } - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) { - master = if_nametoindex(ifa->ifa_name); - freeifaddrs(ifap); - return master; - } - } - freeifaddrs(ifap); - return -1; -} - -int -iface_is_slave_active(struct lldpd *cfg, int master, const char *slave) -{ - char mastername[IFNAMSIZ]; - int active; - if (if_indextoname(master, mastername) == NULL) { - LLOG_WARNX("unable to get master name for %s", - slave); - return 0; /* Safest choice */ - } - if (!iface_is_bond_slave(cfg, slave, mastername, &active)) { - LLOG_WARNX("unable to get slave status for %s", - slave); - return 0; /* Safest choice */ - } - return (active == BOND_STATE_ACTIVE); -} - -void -iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware) -{ - int master, f, state = 0; - FILE *netbond; - const char *slaveif = "Slave Interface: "; - const char *hwaddr = "Permanent HW addr: "; - u_int8_t mac[ETHER_ADDR_LEN]; - char bond[IFNAMSIZ]; - char path[SYSFS_PATH_MAX]; - char line[100]; - if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1) - return; - /* We have a bond, we need to query it to get real MAC addresses */ - if ((if_indextoname(master, bond)) == NULL) { - LLOG_WARNX("unable to get bond name"); - return; - } - - if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s", - bond) >= SYSFS_PATH_MAX) { - LLOG_WARNX("path truncated"); - return; - } - if ((f = priv_open(path)) < 0) { - if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s", - bond) >= SYSFS_PATH_MAX) { - LLOG_WARNX("path truncated"); - return; - } - f = priv_open(path); - } - if (f < 0) { - LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding", - bond); - return; - } - if ((netbond = fdopen(f, "r")) == NULL) { - LLOG_WARN("unable to read stream from %s", path); - close(f); - return; - } - /* State 0: - We parse the file to search "Slave Interface: ". If found, go to - state 1. - State 1: - We parse the file to search "Permanent HW addr: ". If found, we get - the mac. - */ - while (fgets(line, sizeof(line), netbond)) { - switch (state) { - case 0: - if (strncmp(line, slaveif, strlen(slaveif)) == 0) { - if (line[strlen(line)-1] == '\n') - line[strlen(line)-1] = '\0'; - if (strncmp(hardware->h_ifname, - line + strlen(slaveif), - sizeof(hardware->h_ifname)) == 0) - state++; - } - break; - case 1: - if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) { - if (line[strlen(line)-1] == '\n') - line[strlen(line)-1] = '\0'; - if (sscanf(line + strlen(hwaddr), - "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &mac[0], &mac[1], &mac[2], - &mac[3], &mac[4], &mac[5]) != - ETHER_ADDR_LEN) { - LLOG_WARN("unable to parse %s", - line + strlen(hwaddr)); - fclose(netbond); - return; - } - memcpy(hardware->h_lladdr, mac, - ETHER_ADDR_LEN); - fclose(netbond); - return; - } - break; - } - } - LLOG_WARNX("unable to find real mac address for %s", - bond); - fclose(netbond); -} - - -#ifdef ENABLE_LLDPMED - /* Fill in inventory stuff: - - hardware version: /sys/class/dmi/id/product_version - - firmware version: /sys/class/dmi/id/bios_version - - software version: `uname -r` - - serial number: /sys/class/dmi/id/product_serial - - manufacturer: /sys/class/dmi/id/sys_vendor - - model: /sys/class/dmi/id/product_name - - asset: /sys/class/dmi/id/chassis_asset_tag - */ - -char* -dmi_get(char *file) -{ - int dmi, s; - char buffer[100]; - - if ((dmi = priv_open(file)) < 0) { - LLOG_DEBUG("cannot get DMI file %s", file); - return NULL; - } - memset(buffer, 0, sizeof(buffer)); - if ((s = read(dmi, buffer, sizeof(buffer))) == -1) { - LLOG_DEBUG("cannot read DMI file %s", file); - close(dmi); - return NULL; - } - close(dmi); - buffer[sizeof(buffer) - 1] = '\0'; - if ((s > 0) && (buffer[s-1] == '\n')) - buffer[s-1] = '\0'; - if (strlen(buffer)) - return strdup(buffer); - return NULL; -} - -char* -dmi_hw() -{ - return dmi_get(SYSFS_CLASS_DMI "product_version"); -} - -char* -dmi_fw() -{ - return dmi_get(SYSFS_CLASS_DMI "bios_version"); -} - -char* -dmi_sn() -{ - return dmi_get(SYSFS_CLASS_DMI "product_serial"); -} - -char* -dmi_manuf() -{ - return dmi_get(SYSFS_CLASS_DMI "sys_vendor"); -} - -char* -dmi_model() -{ - return dmi_get(SYSFS_CLASS_DMI "product_name"); -} - -char* -dmi_asset() -{ - return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag"); -} -#endif diff --git a/src/interfaces.c b/src/interfaces.c new file mode 100644 index 00000000..a4ea3fd4 --- /dev/null +++ b/src/interfaces.c @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2008 Vincent Bernat + * + * Permission to use, copy, modify, and 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. + */ + +#define INCLUDE_LINUX_IF_H +#include "lldpd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYSFS_PATH_MAX 256 +#define MAX_PORTS 1024 +#define MAX_BRIDGES 1024 + +/* BPF filter to get revelant information from interfaces */ +/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */ +/* FDP: "ether dst 01:e0:52:cc:cc:cc" */ +/* CDP: "ether dst 01:00:0c:cc:cc:cc" */ +/* SONMP: "ether dst 01:00:81:00:01:00" */ +/* EDP: "ether dst 00:e0:2b:00:00:00" */ +#define LLDPD_FILTER_F \ + { 0x28, 0, 0, 0x0000000c }, \ + { 0x15, 0, 4, 0x000088cc }, \ + { 0x20, 0, 0, 0x00000002 }, \ + { 0x15, 0, 2, 0xc200000e }, \ + { 0x28, 0, 0, 0x00000000 }, \ + { 0x15, 11, 12, 0x00000180 }, \ + { 0x20, 0, 0, 0x00000002 }, \ + { 0x15, 0, 2, 0x2b000000 }, \ + { 0x28, 0, 0, 0x00000000 }, \ + { 0x15, 7, 8, 0x000000e0 }, \ + { 0x15, 1, 0, 0x0ccccccc }, \ + { 0x15, 0, 2, 0x81000100 }, \ + { 0x28, 0, 0, 0x00000000 }, \ + { 0x15, 3, 4, 0x00000100 }, \ + { 0x15, 0, 3, 0x52cccccc }, \ + { 0x28, 0, 0, 0x00000000 }, \ + { 0x15, 0, 1, 0x000001e0 }, \ + { 0x6, 0, 0, 0x0000ffff }, \ + { 0x6, 0, 0, 0x00000000 }, +static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F }; + +/* net/if.h */ +extern unsigned int if_nametoindex (__const char *__ifname) __THROW; +extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW; + +static int iface_is_bridge(struct lldpd *, const char *); +static int iface_is_bridged_to(struct lldpd *, + const char *, const char *); +static int iface_is_wireless(struct lldpd *, const char *); +static int iface_is_vlan(struct lldpd *, const char *); +static int iface_is_bond(struct lldpd *, const char *); +static int iface_is_bond_slave(struct lldpd *, + const char *, const char *, int *); +static int iface_is_enslaved(struct lldpd *, const char *); +#if 0 +static int iface_is_slave_active(struct lldpd *, int, const char *); +#endif +static void iface_get_permanent_mac(struct lldpd *, struct lldpd_hardware *); + +static void iface_macphy(struct lldpd_hardware *); +static void iface_mtu(struct lldpd *, struct lldpd_hardware *); +static void iface_multicast(struct lldpd *, const char *, int); +static int iface_eth_init(struct lldpd *, struct lldpd_hardware *); + +static int iface_eth_send(struct lldpd *, struct lldpd_hardware*, char *, size_t); +static int iface_eth_recv(struct lldpd *, struct lldpd_hardware*, int, char*, size_t); +static int iface_eth_close(struct lldpd *, struct lldpd_hardware *); +struct lldpd_ops eth_ops = { + .send = iface_eth_send, + .recv = iface_eth_recv, + .cleanup = iface_eth_close, +}; + +static int +old_iface_is_bridge(struct lldpd *cfg, const char *name) +{ + int ifindices[MAX_BRIDGES]; + char ifname[IFNAMSIZ]; + int num, i; + unsigned long args[3] = { BRCTL_GET_BRIDGES, + (unsigned long)ifindices, MAX_BRIDGES }; + if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 0) { + if (errno != ENOPKG) + LLOG_INFO("unable to get available bridges"); + return 0; + } + for (i = 0; i < num; i++) { + if (if_indextoname(ifindices[i], ifname) == NULL) + LLOG_INFO("unable to get name of interface %d", + ifindices[i]); + else if (strncmp(name, ifname, IFNAMSIZ) == 0) + return 1; + } + return 0; +} + +static int +iface_is_bridge(struct lldpd *cfg, const char *name) +{ + char path[SYSFS_PATH_MAX]; + int f; + + if ((snprintf(path, SYSFS_PATH_MAX, + SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX) + LLOG_WARNX("path truncated"); + if ((f = priv_open(path)) < 0) { + return old_iface_is_bridge(cfg, name); + } + close(f); + return 1; +} + +static int +old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) +{ + int j, index = if_nametoindex(slave); + int ifptindices[MAX_PORTS]; + unsigned long args2[4] = { BRCTL_GET_PORT_LIST, + (unsigned long)ifptindices, MAX_PORTS, 0 }; + struct ifreq ifr; + + strncpy(ifr.ifr_name, master, IFNAMSIZ); + memset(ifptindices, 0, sizeof(ifptindices)); + ifr.ifr_data = (char *)&args2; + + if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0) { + LLOG_WARN("unable to get bridge members for %s", + ifr.ifr_name); + return 0; + } + + for (j = 0; j < MAX_PORTS; j++) { + if (ifptindices[j] == index) + return 1; + } + + return 0; +} + +static int +iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master) +{ + char path[SYSFS_PATH_MAX]; + int f; + + /* Master should be a bridge, first */ + if (!iface_is_bridge(cfg, master)) return 0; + + if (snprintf(path, SYSFS_PATH_MAX, + SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no", + master, slave) >= SYSFS_PATH_MAX) + LLOG_WARNX("path truncated"); + if ((f = priv_open(path)) < 0) { + return old_iface_is_bridged_to(cfg, slave, master); + } + close(f); + return 1; +} + +static int +iface_is_vlan(struct lldpd *cfg, const char *name) +{ + struct vlan_ioctl_args ifv; + memset(&ifv, 0, sizeof(ifv)); + ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; + if ((strlcpy(ifv.device1, name, sizeof(ifv.device1))) >= + sizeof(ifv.device1)) + LLOG_WARNX("device name truncated"); + if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) + return 1; + return 0; +} + +static int +iface_is_wireless(struct lldpd *cfg, const char *name) +{ + struct iwreq iwr; + strlcpy(iwr.ifr_name, name, IFNAMSIZ); + if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) + return 1; + return 0; +} + +static int +iface_is_bond(struct lldpd *cfg, const char *name) +{ + struct ifreq ifr; + struct ifbond ifb; + memset(&ifr, 0, sizeof(ifr)); + memset(&ifb, 0, sizeof(ifb)); + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_data = &ifb; + if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) + return 1; + return 0; +} + +static int +iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master, + int *active) +{ + struct ifreq ifr; + struct ifbond ifb; + struct ifslave ifs; + memset(&ifr, 0, sizeof(ifr)); + memset(&ifb, 0, sizeof(ifb)); + strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); + ifr.ifr_data = &ifb; + if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) { + while (ifb.num_slaves--) { + memset(&ifr, 0, sizeof(ifr)); + memset(&ifs, 0, sizeof(ifs)); + strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name)); + ifr.ifr_data = &ifs; + ifs.slave_id = ifb.num_slaves; + if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) && + (strncmp(ifs.slave_name, slave, sizeof(ifs.slave_name)) == 0)) { + if (active) + *active = ifs.state; + return 1; + } + } + } + return 0; +} + +static int +iface_is_enslaved(struct lldpd *cfg, const char *name) +{ + struct ifaddrs *ifap, *ifa; + int master; + + if (getifaddrs(&ifap) != 0) { + LLOG_WARN("unable to get interface list"); + return -1; + } + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) { + master = if_nametoindex(ifa->ifa_name); + freeifaddrs(ifap); + return master; + } + } + freeifaddrs(ifap); + return -1; +} + +#if 0 +static int +iface_is_slave_active(struct lldpd *cfg, int master, const char *slave) +{ + char mastername[IFNAMSIZ]; + int active; + if (if_indextoname(master, mastername) == NULL) { + LLOG_WARNX("unable to get master name for %s", + slave); + return 0; /* Safest choice */ + } + if (!iface_is_bond_slave(cfg, slave, mastername, &active)) { + LLOG_WARNX("unable to get slave status for %s", + slave); + return 0; /* Safest choice */ + } + return (active == BOND_STATE_ACTIVE); +} +#endif + +static void +iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + int master, f, state = 0; + FILE *netbond; + const char *slaveif = "Slave Interface: "; + const char *hwaddr = "Permanent HW addr: "; + u_int8_t mac[ETHER_ADDR_LEN]; + char bond[IFNAMSIZ]; + char path[SYSFS_PATH_MAX]; + char line[100]; + if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1) + return; + /* We have a bond, we need to query it to get real MAC addresses */ + if ((if_indextoname(master, bond)) == NULL) { + LLOG_WARNX("unable to get bond name"); + return; + } + + if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s", + bond) >= SYSFS_PATH_MAX) { + LLOG_WARNX("path truncated"); + return; + } + if ((f = priv_open(path)) < 0) { + if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s", + bond) >= SYSFS_PATH_MAX) { + LLOG_WARNX("path truncated"); + return; + } + f = priv_open(path); + } + if (f < 0) { + LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding", + bond); + return; + } + if ((netbond = fdopen(f, "r")) == NULL) { + LLOG_WARN("unable to read stream from %s", path); + close(f); + return; + } + /* State 0: + We parse the file to search "Slave Interface: ". If found, go to + state 1. + State 1: + We parse the file to search "Permanent HW addr: ". If found, we get + the mac. + */ + while (fgets(line, sizeof(line), netbond)) { + switch (state) { + case 0: + if (strncmp(line, slaveif, strlen(slaveif)) == 0) { + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + if (strncmp(hardware->h_ifname, + line + strlen(slaveif), + sizeof(hardware->h_ifname)) == 0) + state++; + } + break; + case 1: + if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) { + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + if (sscanf(line + strlen(hwaddr), + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]) != + ETHER_ADDR_LEN) { + LLOG_WARN("unable to parse %s", + line + strlen(hwaddr)); + fclose(netbond); + return; + } + memcpy(hardware->h_lladdr, mac, + ETHER_ADDR_LEN); + fclose(netbond); + return; + } + break; + } + } + LLOG_WARNX("unable to find real mac address for %s", + bond); + fclose(netbond); +} + +/* Fill up MAC/PHY for a given hardware port */ +static void +iface_macphy(struct lldpd_hardware *hardware) +{ +#ifdef ENABLE_DOT3 + struct ethtool_cmd ethc; + struct lldpd_port *port = &hardware->h_lport; + int j; + int advertised_ethtool_to_rfc3636[][2] = { + {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T}, + {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD}, + {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX}, + {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD}, + {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T}, + {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD}, + {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, + {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE}, + {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE}, + {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, + {0,0}}; + + if (priv_ethtool(hardware->h_ifname, ðc) == 0) { + port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0; + port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1; + for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) { + if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0]) + port->p_autoneg_advertised |= + advertised_ethtool_to_rfc3636[j][1]; + } + switch (ethc.speed) { + case SPEED_10: + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD; + if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2; + if (ethc.port == PORT_FIBRE) + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD; + break; + case SPEED_100: + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD; + if (ethc.port == PORT_BNC) + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD; + if (ethc.port == PORT_FIBRE) + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD; + break; + case SPEED_1000: + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD; + if (ethc.port == PORT_FIBRE) + port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ + LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD; + break; + case SPEED_10000: + port->p_mau_type = (ethc.port == PORT_FIBRE) ? \ + LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER; + break; + } + if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI; + } +#endif +} + +static void +iface_mtu(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + struct ifreq ifr; + + /* get MTU */ + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name)); + if (ioctl(cfg->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) { + LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname); + hardware->h_mtu = 1500; + } else + hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu; +} + +static void +iface_multicast(struct lldpd *cfg, const char *name, int remove) +{ + int i, rc; + + for (i=0; cfg->g_protocols[i].mode != 0; i++) { + if (!cfg->g_protocols[i].enabled) continue; + if ((rc = priv_iface_multicast(name, + cfg->g_protocols[i].mac, !remove)) != 0) { + errno = rc; + if (errno != ENOENT) + LLOG_INFO("unable to %s %s address to multicast filter for %s", + (remove)?"delete":"add", + cfg->g_protocols[i].name, + name); + } + } +} + +static int +iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + int status; + const struct sock_fprog prog = { + .filter = lldpd_filter_f, + .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter) + }; + + status = priv_iface_eth_init(hardware); + if (status != 0) + return status; + + /* fd to receive is the same as fd to send */ + FD_SET(hardware->h_sendfd, &hardware->h_recvfds); + + /* Set filter */ + if (setsockopt(hardware->h_sendfd, SOL_SOCKET, SO_ATTACH_FILTER, + &prog, sizeof(prog)) < 0) { + LLOG_WARN("unable to change filter for %s", hardware->h_ifname); + return ENETDOWN; + } + + iface_multicast(cfg, hardware->h_ifname, 0); + + LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname, + hardware->h_sendfd); + return 0; +} + +static int +iface_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, + char *buffer, size_t size) +{ + return write(hardware->h_sendfd, + buffer, size); +} + +static int +iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, + int fd, char *buffer, size_t size) +{ + int n; + struct sockaddr_ll from; + socklen_t fromlen; + + fromlen = sizeof(from); + if ((n = recvfrom(fd, + buffer, + size, 0, + (struct sockaddr *)&from, + &fromlen)) == -1) { + LLOG_WARN("error while receiving frame on %s", + hardware->h_ifname); + hardware->h_rx_discarded_cnt++; + return -1; + } + if (from.sll_pkttype == PACKET_OUTGOING) + return -1; + return n; +} + +static int +iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) +{ + close(hardware->h_sendfd); + iface_multicast(cfg, hardware->h_ifname, 1); + return 0; +} + +void +lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap) +{ + struct ifaddrs *ifa; + struct sockaddr_ll *sdl; + struct lldpd_hardware *hardware; + struct lldpd_port *port; + u_int8_t *lladdr; + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + /* First, check if this interface has already been handled */ + if (!ifa->ifa_flags) + continue; + + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != PF_PACKET) + continue; + + sdl = (struct sockaddr_ll *)ifa->ifa_addr; + if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen) + continue; + + if (iface_is_bridge(cfg, ifa->ifa_name)) { + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE; + continue; + } + + /* If the interface is a bond or a VLAN, we don't handle it + * here. If it is enslaved, it should have been handled earlier, + * therefore, we don't test that here. */ + if ((iface_is_vlan(cfg, ifa->ifa_name)) || + (iface_is_bond(cfg, ifa->ifa_name))) + continue; + + /* We request that the interface is able to do either multicast + * or broadcast to be able to send discovery frames. */ + if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST))) + continue; + + if (iface_is_wireless(cfg, ifa->ifa_name)) + LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN; + + if ((hardware = lldpd_get_hardware(cfg, ifa->ifa_name)) == NULL) { + if ((hardware = lldpd_alloc_hardware(cfg, + ifa->ifa_name)) == NULL) { + LLOG_WARNX("Unable to allocate space for %s", + ifa->ifa_name); + continue; + } + if (iface_eth_init(cfg, hardware) != 0) { + LLOG_WARN("unable to initialize %s", hardware->h_ifname); + lldpd_hardware_cleanup(cfg, hardware); + continue; + } + hardware->h_ops = ð_ops; + TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); + } else + lldpd_port_cleanup(&hardware->h_lport, 0); + + port = &hardware->h_lport; + hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */ + ifa->ifa_flags = 0; /* Future handlers + don't have to + care about this + interface. */ + + /* Get local address */ + lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr); + memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr)); + iface_get_permanent_mac(cfg, hardware); + + /* Port ID is the same as hardware address */ + port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR; + if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL) + fatal(NULL); + memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr)); + port->p_id_len = sizeof(hardware->h_lladdr); + + /* Port description is its name */ + port->p_descr = strdup(hardware->h_ifname); + + /* Fill additional info */ + iface_macphy(hardware); + iface_mtu(cfg, hardware); + } +} + +void +lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap) +{ +#ifdef ENABLE_DOT1 + struct ifaddrs *ifa; + struct lldpd_vlan *vlan; + struct vlan_ioctl_args ifv; + struct lldpd_hardware *hardware; + struct lldpd_port *port; + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_flags) + continue; + if (!iface_is_vlan(cfg, ifa->ifa_name)) + continue; + + /* We need to find the physical interfaces of this + vlan, through bonds and bridges. */ + memset(&ifv, 0, sizeof(ifv)); + ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; + strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1)); + if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) { + /* Three cases: + 1. we get a real device + 2. we get a bond + 3. we get a bridge + */ + if ((hardware = lldpd_get_hardware(cfg, + ifv.u.device2)) == NULL) { + if (iface_is_bond(cfg, ifv.u.device2)) { + TAILQ_FOREACH(hardware, &cfg->g_hardware, + h_entries) + if (iface_is_bond_slave(cfg, + hardware->h_ifname, + ifv.u.device2, NULL)) + break; + } else if (iface_is_bridge(cfg, ifv.u.device2)) { + TAILQ_FOREACH(hardware, &cfg->g_hardware, + h_entries) + if (iface_is_bridged_to(cfg, + hardware->h_ifname, + ifv.u.device2)) + break; + } + } + if (!hardware) continue; + port = &hardware->h_lport; + if ((vlan = (struct lldpd_vlan *) + calloc(1, sizeof(struct lldpd_vlan))) == NULL) + continue; + if ((vlan->v_name = strdup(ifa->ifa_name)) == NULL) { + free(vlan); + continue; + } + memset(&ifv, 0, sizeof(ifv)); + ifv.cmd = GET_VLAN_VID_CMD; + strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1)); + if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) { + /* Dunno what happened */ + free(vlan->v_name); + free(vlan); + } else { + vlan->v_vid = ifv.u.VID; + TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries); + } + } + } +#endif +} + +/* Find a management address in all available interfaces, even those that were + already handled. This is a special interface handler because it does not + really handle interface related information (management address is attached + to the local chassis). */ +void +lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap) +{ + struct ifaddrs *ifa; + struct sockaddr_in *sa; + + if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr != INADDR_ANY) + return; /* We already have one */ + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_addr != NULL) && + (ifa->ifa_addr->sa_family == AF_INET)) { + /* We have an IPv4 address (IPv6 not handled yet) */ + sa = (struct sockaddr_in *)ifa->ifa_addr; + if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) && + (cfg->g_mgmt_pattern == NULL)) { + memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, + &sa->sin_addr, + sizeof(struct in_addr)); + LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name); + } else if (cfg->g_mgmt_pattern != NULL) { + char *ip; + ip = inet_ntoa(sa->sin_addr); + if (fnmatch(cfg->g_mgmt_pattern, + ip, 0) == 0) { + memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, + &sa->sin_addr, + sizeof(struct in_addr)); + LOCAL_CHASSIS(cfg)->c_mgmt_if = + if_nametoindex(ifa->ifa_name); + } + } + } + } +} diff --git a/src/lldp.c b/src/lldp.c index d19ac324..5aa9deee 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -265,8 +265,8 @@ lldp_send(struct lldpd *global, POKE_END_LLDP_TLV)) goto toobig; - if (write(hardware->h_raw, packet, - pos - packet) == -1) { + if (hardware->h_ops->send(global, hardware, + (char *)packet, pos - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", hardware->h_ifname); free(packet); diff --git a/src/lldpd.c b/src/lldpd.c index 034f47a2..ee3ce70d 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -32,12 +31,7 @@ #include #include #include -#include #include -#include -#include -#include -#include #ifdef USE_SNMP #include @@ -48,38 +42,6 @@ static void usage(void); -static int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *); -static void lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *); -static int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *); -static void lldpd_iface_multicast(struct lldpd *, const char *, int); - -/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */ -/* FDP: "ether dst 01:e0:52:cc:cc:cc" */ -/* CDP: "ether dst 01:00:0c:cc:cc:cc" */ -/* SONMP: "ether dst 01:00:81:00:01:00" */ -/* EDP: "ether dst 00:e0:2b:00:00:00" */ -#define LLDPD_FILTER_F \ - { 0x28, 0, 0, 0x0000000c }, \ - { 0x15, 0, 4, 0x000088cc }, \ - { 0x20, 0, 0, 0x00000002 }, \ - { 0x15, 0, 2, 0xc200000e }, \ - { 0x28, 0, 0, 0x00000000 }, \ - { 0x15, 11, 12, 0x00000180 }, \ - { 0x20, 0, 0, 0x00000002 }, \ - { 0x15, 0, 2, 0x2b000000 }, \ - { 0x28, 0, 0, 0x00000000 }, \ - { 0x15, 7, 8, 0x000000e0 }, \ - { 0x15, 1, 0, 0x0ccccccc }, \ - { 0x15, 0, 2, 0x81000100 }, \ - { 0x28, 0, 0, 0x00000000 }, \ - { 0x15, 3, 4, 0x00000100 }, \ - { 0x15, 0, 3, 0x52cccccc }, \ - { 0x28, 0, 0, 0x00000000 }, \ - { 0x15, 0, 1, 0x000001e0 }, \ - { 0x6, 0, 0, 0x0000ffff }, \ - { 0x6, 0, 0, 0x00000000 }, -static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F }; - static struct protocol protos[] = { { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL, @@ -106,8 +68,9 @@ static struct protocol protos[] = {0,0,0,0,0,0} } }; -static -struct lldpd_hardware *lldpd_hardware_add(struct lldpd *, struct ifaddrs *); +static void lldpd_update_localchassis(struct lldpd *); +static void lldpd_update_localports(struct lldpd *); +static void lldpd_cleanup(struct lldpd *); static void lldpd_loop(struct lldpd *); static void lldpd_shutdown(int); static void lldpd_exit(); @@ -133,79 +96,41 @@ usage(void) exit(1); } -static void -lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware) -{ - struct ifreq ifr; - - /* get MTU */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name)); - if (ioctl(global->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) { - LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname); - hardware->h_mtu = 1500; - } else - hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu; -} - -static int -lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware) -{ - int status; - struct sock_fprog prog; - - lldpd_iface_init_mtu(global, hardware); - status = priv_iface_init(hardware, -1); - if (status != 0) - return status; - - /* Set filter */ - prog.filter = lldpd_filter_f; - prog.len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter); - if (setsockopt(hardware->h_raw, SOL_SOCKET, SO_ATTACH_FILTER, - &prog, sizeof(prog)) < 0) { - LLOG_WARN("unable to change filter for %s", hardware->h_ifname); - return ENETDOWN; - } - - lldpd_iface_multicast(global, hardware->h_ifname, 0); - - LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname, - hardware->h_raw); - return 0; -} - -static void -lldpd_iface_multicast(struct lldpd *global, const char *name, int remove) +struct lldpd_hardware * +lldpd_get_hardware(struct lldpd *cfg, char *name) { - int i, rc; - - for (i=0; global->g_protocols[i].mode != 0; i++) { - if (!global->g_protocols[i].enabled) continue; - if ((rc = priv_iface_multicast(name, - global->g_protocols[i].mac, !remove)) != 0) { - errno = rc; - if (errno != ENOENT) - LLOG_INFO("unable to %s %s address to multicast filter for %s", - (remove)?"delete":"add", - global->g_protocols[i].name, - name); - } + struct lldpd_hardware *hardware; + TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { + if (strcmp(hardware->h_ifname, name) == 0) + break; } + return hardware; } -static int -lldpd_iface_close(struct lldpd *global, struct lldpd_hardware *hardware) +struct lldpd_hardware * +lldpd_alloc_hardware(struct lldpd *cfg, char *name) { - char listen[IFNAMSIZ]; + struct lldpd_hardware *hardware; - close(hardware->h_raw); - hardware->h_raw = -1; + if ((hardware = (struct lldpd_hardware *) + calloc(1, sizeof(struct lldpd_hardware))) == NULL) + return NULL; - memcpy(listen, hardware->h_ifname, IFNAMSIZ); - lldpd_iface_multicast(global, listen, 1); + strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname)); + hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg); + TAILQ_INIT(&hardware->h_rports); - return 0; +#ifdef ENABLE_LLDPMED + if (LOCAL_CHASSIS(cfg)->c_med_cap_available) { + hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP; + if (!cfg->g_noinventory) + hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV; + } +#endif +#ifdef ENABLE_DOT1 + TAILQ_INIT(&hardware->h_lport.p_vlans); +#endif + return hardware; } #ifdef ENABLE_DOT1 @@ -291,13 +216,26 @@ lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all } void -lldpd_hardware_cleanup(struct lldpd_hardware *hardware) +lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) { + int i; lldpd_port_cleanup(&hardware->h_lport, 1); + /* If we have a dedicated cleanup function, use it. Otherwise, + we just free the hardware-dependent data and close all FD + in h_recvfds and h_sendfd. */ + if (hardware->h_ops->cleanup) + hardware->h_ops->cleanup(cfg, hardware); + else { + free(hardware->h_data); + for (i=0; i < FD_SETSIZE; i++) + if (FD_ISSET(i, &hardware->h_recvfds)) + close(i); + if (hardware->h_sendfd) close(hardware->h_sendfd); + } free(hardware); } -void +static void lldpd_cleanup(struct lldpd *cfg) { struct lldpd_hardware *hardware, *hardware_next; @@ -305,213 +243,15 @@ lldpd_cleanup(struct lldpd *cfg) for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; hardware = hardware_next) { hardware_next = TAILQ_NEXT(hardware, h_entries); - if (hardware->h_flags == 0) { + if (!hardware->h_flags) { TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries); - lldpd_iface_close(cfg, hardware); lldpd_remote_cleanup(cfg, hardware, 1); - lldpd_hardware_cleanup(hardware); + lldpd_hardware_cleanup(cfg, hardware); } else lldpd_remote_cleanup(cfg, hardware, 0); } } -static struct lldpd_hardware * -lldpd_hardware_add(struct lldpd *cfg, struct ifaddrs *ifa) -{ -#if defined (ENABLE_DOT1) || defined (ENABLE_DOT3) - struct ifaddrs *oifap, *oifa; -#endif - struct lldpd_hardware *hardware; - struct lldpd_port *port; -#ifdef ENABLE_DOT1 - struct lldpd_vlan *vlan; - struct vlan_ioctl_args ifv; -#endif -#ifdef ENABLE_DOT3 - struct ethtool_cmd ethc; -#endif - u_int8_t *lladdr; - - TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { - if (strcmp(hardware->h_ifname, ifa->ifa_name) == 0) - break; - } - - if (hardware == NULL) { - if ((hardware = (struct lldpd_hardware *) - calloc(1, sizeof(struct lldpd_hardware))) == NULL) - return (NULL); - hardware->h_raw = -1; - hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg); - TAILQ_INIT(&hardware->h_rports); -#ifdef ENABLE_LLDPMED - if (LOCAL_CHASSIS(cfg)->c_med_cap_available) { - hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP; - if (!cfg->g_noinventory) - hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV; - } -#endif -#ifdef ENABLE_DOT1 - TAILQ_INIT(&hardware->h_lport.p_vlans); - } else { - lldpd_port_cleanup(&hardware->h_lport, 0); -#endif - } - - port = &hardware->h_lport; - hardware->h_flags = ifa->ifa_flags; - - strlcpy(hardware->h_ifname, ifa->ifa_name, sizeof(hardware->h_ifname)); - lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr); - memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr)); - iface_get_permanent_mac(cfg, hardware); - port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR; - if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL) - fatal(NULL); - memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr)); - port->p_id_len = sizeof(hardware->h_lladdr); - port->p_descr = strdup(hardware->h_ifname); - - if (LOCAL_CHASSIS(cfg)->c_id == NULL) { - /* Use the first port's l2 addr as the chassis ID */ - if ((LOCAL_CHASSIS(cfg)->c_id = - malloc(sizeof(hardware->h_lladdr))) == NULL) - fatal(NULL); - LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; - LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr); - memcpy(LOCAL_CHASSIS(cfg)->c_id, - hardware->h_lladdr, sizeof(hardware->h_lladdr)); - } - - /* Get VLANS and aggregation status */ -#if defined (ENABLE_DOT3) || defined (ENABLE_DOT1) - if (getifaddrs(&oifap) != 0) - fatal("lldpd_hardware_add: failed to get interface list"); - for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) { -#ifdef ENABLE_DOT1 - /* Check if we already have checked this one */ - int skip = 0; - TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) { - if (strcmp(vlan->v_name, oifa->ifa_name) == 0) { - skip = 1; - break; - } - } - if (skip) continue; -#endif - - /* Aggregation check */ -#ifdef ENABLE_DOT3 - if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL)) - port->p_aggregid = if_nametoindex(oifa->ifa_name); -#endif - -#ifdef ENABLE_DOT1 - /* VLAN check */ - memset(&ifv, 0, sizeof(ifv)); - ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; - strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1)); - if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) && - ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) || - (iface_is_bridged_to(cfg, hardware->h_ifname, ifv.u.device2)) || - (strncmp(hardware->h_ifname, ifv.u.device2, sizeof(ifv.u.device2)) == 0))) { - if ((vlan = (struct lldpd_vlan *) - calloc(1, sizeof(struct lldpd_vlan))) == NULL) - continue; - if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) { - free(vlan); - continue; - } - memset(&ifv, 0, sizeof(ifv)); - ifv.cmd = GET_VLAN_VID_CMD; - strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1)); - if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) { - /* Dunno what happened */ - free(vlan->v_name); - free(vlan); - } else { - vlan->v_vid = ifv.u.VID; - TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries); - } - } -#endif - } - freeifaddrs(oifap); -#endif - -#ifdef ENABLE_DOT3 - /* MAC/PHY */ - if (priv_ethtool(hardware->h_ifname, ðc) == 0) { - int j; - int advertised_ethtool_to_rfc3636[][2] = { - {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T}, - {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD}, - {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX}, - {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD}, - {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T}, - {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD}, - {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, - {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE}, - {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE}, - {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER}, - {0,0}}; - - port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0; - port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1; - for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) { - if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0]) - port->p_autoneg_advertised |= advertised_ethtool_to_rfc3636[j][1]; - } - switch (ethc.speed) { - case SPEED_10: - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD; - if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2; - if (ethc.port == PORT_FIBRE) - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD; - break; - case SPEED_100: - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD; - if (ethc.port == PORT_BNC) - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD; - if (ethc.port == PORT_FIBRE) - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD; - break; - case SPEED_1000: - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD; - if (ethc.port == PORT_FIBRE) - port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \ - LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD; - break; - case SPEED_10000: - port->p_mau_type = (ethc.port == PORT_FIBRE) ? \ - LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER; - break; - } - if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI; - } else - LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname); -#endif - - if (!INTERFACE_OPENED(hardware)) { - - if (lldpd_iface_init(cfg, hardware) != 0) { - LLOG_WARN("unable to initialize %s", hardware->h_ifname); - lldpd_hardware_cleanup(hardware); - return (NULL); - } - - TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries); - } - - return (hardware); -} - static int lldpd_guess_type(struct lldpd *cfg, char *frame, int s) { @@ -662,8 +402,6 @@ lldpd_recv_all(struct lldpd *cfg) struct lldpd_client *client, *client_next; fd_set rfds; struct timeval tv; - struct sockaddr_ll from; - socklen_t fromlen; #ifdef USE_SNMP int fakeblock = 0; struct timeval *tvp = &tv; @@ -684,12 +422,16 @@ lldpd_recv_all(struct lldpd *cfg) TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { /* Ignore if interface is down */ - if (((hardware->h_flags & IFF_UP) == 0) || - ((hardware->h_flags & IFF_RUNNING) == 0)) + if ((hardware->h_flags & IFF_RUNNING) == 0) continue; - FD_SET(hardware->h_raw, &rfds); - if (nfds < hardware->h_raw) - nfds = hardware->h_raw; + /* This is quite expensive but we don't rely on internal + * structure of fd_set. */ + for (n = 0; n < FD_SETSIZE; n++) + if (FD_ISSET(n, &hardware->h_recvfds)) { + FD_SET(n, &rfds); + if (nfds < n) + nfds = n; + } } TAILQ_FOREACH(client, &cfg->g_clients, next) { FD_SET(client->fd, &rfds); @@ -725,38 +467,24 @@ lldpd_recv_all(struct lldpd *cfg) } #endif /* USE_SNMP */ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { - /* We could have received something on _real_ - * interface. However, even in this case, this could be - * just an outgoing packet. We will try to handle both - * cases, but maybe not in the same select. */ - if (FD_ISSET(hardware->h_raw, &rfds)) { - if ((buffer = (char *)malloc( - hardware->h_mtu)) == NULL) { - LLOG_WARN("failed to alloc reception buffer"); - continue; - } - fromlen = sizeof(from); - if ((n = recvfrom( - hardware->h_raw, - buffer, - hardware->h_mtu, 0, - (struct sockaddr *)&from, - &fromlen)) == -1) { - LLOG_WARN("error while receiving frame on %s", - hardware->h_ifname); - hardware->h_rx_discarded_cnt++; - free(buffer); - continue; - } - if (from.sll_pkttype == PACKET_OUTGOING) { - free(buffer); - continue; - } - hardware->h_rx_cnt++; - lldpd_decode(cfg, buffer, n, hardware); + for (n = 0; n < FD_SETSIZE; n++) + if ((FD_ISSET(n, &hardware->h_recvfds)) && + (FD_ISSET(n, &rfds))) break; + if (n == FD_SETSIZE) continue; + if ((buffer = (char *)malloc( + hardware->h_mtu)) == NULL) { + LLOG_WARN("failed to alloc reception buffer"); + continue; + } + if ((n = hardware->h_ops->recv(cfg, hardware, + n, buffer, hardware->h_mtu)) == -1) { free(buffer); + continue; } - + hardware->h_rx_cnt++; + lldpd_decode(cfg, buffer, n, hardware); + free(buffer); + break; } if (FD_ISSET(cfg->g_ctl, &rfds)) { if (ctl_accept(cfg, cfg->g_ctl) == -1) @@ -806,8 +534,7 @@ lldpd_send_all(struct lldpd *cfg) cfg->g_lastsent = time(NULL); TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { /* Ignore if interface is down */ - if (((hardware->h_flags & IFF_UP) == 0) || - ((hardware->h_flags & IFF_RUNNING) == 0)) + if ((hardware->h_flags & IFF_RUNNING) == 0) continue; for (i=0; cfg->g_protocols[i].mode != 0; i++) { @@ -853,20 +580,16 @@ lldpd_med(struct lldpd_chassis *chassis) #endif static void -lldpd_loop(struct lldpd *cfg) +lldpd_update_localchassis(struct lldpd *cfg) { - struct ifaddrs *ifap, *ifa; - struct sockaddr_ll *sdl; - struct lldpd_hardware *hardware; + struct utsname un; + char *hp; int f; char status; - struct utsname *un; - char *hp; + struct lldpd_hardware *hardware; /* Set system name and description */ - if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL) - fatal(NULL); - if (uname(un) != 0) + if (uname(&un) != 0) fatal("failed to get system information"); if ((hp = priv_gethostbyname()) == NULL) fatal("failed to get system name"); @@ -875,7 +598,7 @@ lldpd_loop(struct lldpd *cfg) if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL) fatal(NULL); if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s", - un->sysname, un->release, un->version, un->machine) == -1) + un.sysname, un.release, un.version, un.machine) == -1) fatal("failed to set system description"); /* Check forwarding */ @@ -891,77 +614,72 @@ lldpd_loop(struct lldpd *cfg) LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE; lldpd_med(LOCAL_CHASSIS(cfg)); free(LOCAL_CHASSIS(cfg)->c_med_sw); - LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un->release); + LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release); #endif - free(un); + /* Set chassis ID if needed */ + if ((LOCAL_CHASSIS(cfg)->c_id == NULL) && + (hardware = TAILQ_FIRST(&cfg->g_hardware))) { + if ((LOCAL_CHASSIS(cfg)->c_id = + malloc(sizeof(hardware->h_lladdr))) == NULL) + fatal(NULL); + LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; + LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr); + memcpy(LOCAL_CHASSIS(cfg)->c_id, + hardware->h_lladdr, sizeof(hardware->h_lladdr)); + } +} + +static void +lldpd_update_localports(struct lldpd *cfg) +{ + struct ifaddrs *ifap; + struct lldpd_hardware *hardware; + lldpd_ifhandlers ifhs[] = { + lldpd_ifh_eth, /* Handle classic ethernet interfaces */ + lldpd_ifh_vlan, /* Handle VLAN */ + lldpd_ifh_mgmt, /* Handle management address (if not already handled) */ + NULL + }; + lldpd_ifhandlers *ifh; + + /* h_flags is set to 0 for each port. If the port is updated, h_flags + * will be set to a non-zero value. This will allow us to clean up any + * non up-to-date port */ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) hardware->h_flags = 0; - if (getifaddrs(&ifap) != 0) - fatal("lldpd_loop: failed to get interface list"); - LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY; - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr == INADDR_ANY) - /* Get management address, if available */ - if ((ifa->ifa_addr != NULL) && - (ifa->ifa_addr->sa_family == AF_INET)) { - struct sockaddr_in *sa; - sa = (struct sockaddr_in *)ifa->ifa_addr; - if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) && - (cfg->g_mgmt_pattern == NULL)) { - memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, - &sa->sin_addr, - sizeof(struct in_addr)); - LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name); - } - else if (cfg->g_mgmt_pattern != NULL) { - char *ip; - ip = inet_ntoa(sa->sin_addr); - if (fnmatch(cfg->g_mgmt_pattern, - ip, 0) == 0) { - memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, - &sa->sin_addr, - sizeof(struct in_addr)); - LOCAL_CHASSIS(cfg)->c_mgmt_if = - if_nametoindex(ifa->ifa_name); - } - } - } - - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != PF_PACKET) - continue; - - sdl = (struct sockaddr_ll *)ifa->ifa_addr; - if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen) - continue; - - if (iface_is_bridge(cfg, ifa->ifa_name)) { - LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE; - continue; - } - - if ((iface_is_vlan(cfg, ifa->ifa_name)) || - (iface_is_bond(cfg, ifa->ifa_name))) - continue; - - if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST))) - continue; - - if (iface_is_wireless(cfg, ifa->ifa_name)) - LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN; - - if (lldpd_hardware_add(cfg, ifa) == NULL) - LLOG_WARNX("failed to allocate port %s, skip it", - ifa->ifa_name); - } - + if (getifaddrs(&ifap) != 0) + fatal("lldpd_update_localports: failed to get interface list"); + + /* We will run the list of interfaces through a list of interface + * handlers. Each handler will create or update some hardware port (and + * will set h_flags to a non zero value. The handler can use the list of + * interfaces but this is not mandatory. If the interface handler + * handles an interface from the list, it should set ifa_flags to 0 to + * let know the other handlers that it took care of this interface. This + * means that more specific handlers should be before less specific + * ones. */ + for (ifh = ifhs; *ifh != NULL; ifh++) + (*ifh)(cfg, ifap); freeifaddrs(ifap); +} +static void +lldpd_loop(struct lldpd *cfg) +{ + /* Main loop. + + 1. Update local ports information + 2. Clean unwanted (removed) local ports + 3. Update local chassis information + 4. Send packets + 5. Receive packets + */ + lldpd_update_localports(cfg); lldpd_cleanup(cfg); - + lldpd_update_localchassis(cfg); lldpd_send_all(cfg); lldpd_recv_all(cfg); } @@ -979,12 +697,13 @@ static struct lldpd *gcfg = NULL; static void lldpd_exit() { - struct lldpd_hardware *hardware; + struct lldpd_hardware *hardware, *hardware_next; close(gcfg->g_ctl); priv_ctl_cleanup(); - TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) { - if (INTERFACE_OPENED(hardware)) - lldpd_iface_close(gcfg, hardware); + for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL; + hardware = hardware_next) { + hardware_next = TAILQ_NEXT(hardware, h_entries); + lldpd_hardware_cleanup(gcfg, hardware); } #ifdef USE_SNMP if (gcfg->g_snmp) diff --git a/src/lldpd.h b/src/lldpd.h index 4765a6fb..a5cebbd7 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -31,6 +31,7 @@ #include #include #endif +#include #include #include #include @@ -193,15 +194,33 @@ struct lldpd_frame { unsigned char frame[]; }; +struct lldpd_hardware; +struct lldpd; +struct lldpd_ops { + int(*send)(struct lldpd *, + struct lldpd_hardware*, + char *, size_t); /* Function to send a frame */ + int(*recv)(struct lldpd *, + struct lldpd_hardware*, + int, char *, size_t); /* Function to receive a frame */ + int(*cleanup)(struct lldpd *, struct lldpd_hardware *); /* Cleanup function. */ +}; + struct lldpd_hardware { TAILQ_ENTRY(lldpd_hardware) h_entries; -#define INTERFACE_OPENED(x) ((x)->h_raw != -1) - int h_raw; + fd_set h_recvfds; /* FD for reception */ + int h_sendfd; /* FD for sending, only used by h_ops */ + struct lldpd_ops *h_ops; /* Hardware-dependent functions */ + void *h_data; /* Hardware-dependent data */ - int h_flags; int h_mtu; - char h_ifname[IFNAMSIZ]; + int h_flags; /* Packets will be sent only + if IFF_RUNNING. Will be + removed if this is left + to 0. */ + int h_ifindex; /* Interface index, used by SNMP */ + char h_ifname[IFNAMSIZ]; /* Should be unique */ u_int8_t h_lladdr[ETHER_ADDR_LEN]; u_int64_t h_tx_cnt; @@ -229,7 +248,6 @@ struct lldpd_client { #define PROTO_DECODE_SIG struct lldpd *, char *, int, struct lldpd_hardware *, struct lldpd_chassis **, struct lldpd_port ** #define PROTO_GUESS_SIG char *, int -struct lldpd; struct protocol { #define LLDPD_MODE_LLDP 1 #define LLDPD_MODE_CDPV1 2 @@ -275,6 +293,8 @@ struct lldpd { TAILQ_HEAD(, lldpd_hardware) g_hardware; }; +typedef void(*lldpd_ifhandlers)(struct lldpd *, struct ifaddrs *); + enum hmsg_type { HMSG_NONE, HMSG_GET_INTERFACES, @@ -301,8 +321,9 @@ struct hmsg { #define MAX_HMSGSIZE 8192 /* lldpd.c */ -void lldpd_cleanup(struct lldpd *); -void lldpd_hardware_cleanup(struct lldpd_hardware *); +struct lldpd_hardware *lldpd_get_hardware(struct lldpd *, char *); +struct lldpd_hardware *lldpd_alloc_hardware(struct lldpd *, char *); +void lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *); #ifdef ENABLE_DOT1 void lldpd_vlan_cleanup(struct lldpd_port *); #endif @@ -354,18 +375,12 @@ int ctl_msg_unpack_list(char *, void *, unsigned int, struct hmsg *, void **); int ctl_msg_pack_structure(char *, void *, unsigned int, struct hmsg *, void **); int ctl_msg_unpack_structure(char *, void *, unsigned int, struct hmsg *, void **); -/* features.c */ -int iface_is_bridge(struct lldpd *, const char *); -int iface_is_bridged_to(struct lldpd *, - const char *, const char *); -int iface_is_wireless(struct lldpd *, const char *); -int iface_is_vlan(struct lldpd *, const char *); -int iface_is_bond(struct lldpd *, const char *); -int iface_is_bond_slave(struct lldpd *, - const char *, const char *, int *); -int iface_is_enslaved(struct lldpd *, const char *); -int iface_is_slave_active(struct lldpd *, int, const char *); -void iface_get_permanent_mac(struct lldpd *, struct lldpd_hardware *); +/* interfaces.c */ +void lldpd_ifh_eth(struct lldpd *, struct ifaddrs *); +void lldpd_ifh_vlan(struct lldpd *, struct ifaddrs *); +void lldpd_ifh_mgmt(struct lldpd *, struct ifaddrs *); + +/* dmi.c */ #ifdef ENABLE_LLDPMED char *dmi_hw(); char *dmi_fw(); @@ -422,7 +437,7 @@ void priv_ctl_cleanup(); char *priv_gethostbyname(); int priv_open(char*); int priv_ethtool(char*, struct ethtool_cmd*); -int priv_iface_init(struct lldpd_hardware *, int); +int priv_iface_eth_init(struct lldpd_hardware *); int priv_iface_multicast(const char *, u_int8_t *, int); int priv_snmp_socket(struct sockaddr_un *); diff --git a/src/priv.c b/src/priv.c index 219ef1dd..00916892 100644 --- a/src/priv.c +++ b/src/priv.c @@ -152,17 +152,16 @@ priv_ethtool(char *ifname, struct ethtool_cmd *ethc) } int -priv_iface_init(struct lldpd_hardware *hardware, int master) +priv_iface_eth_init(struct lldpd_hardware *hardware) { int cmd, rc; cmd = PRIV_IFACE_INIT; must_write(remote, &cmd, sizeof(int)); - must_write(remote, &master, sizeof(int)); must_write(remote, hardware->h_ifname, IFNAMSIZ); must_read(remote, &rc, sizeof(int)); if (rc != 0) return rc; /* It's errno */ - hardware->h_raw = receive_fd(remote); + hardware->h_sendfd = receive_fd(remote); return 0; } @@ -328,8 +327,6 @@ asroot_ethtool() ifr.ifr_data = (caddr_t)ðc; ethc.cmd = ETHTOOL_GSET; if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) { - LLOG_DEBUG("[priv]: unable to ioctl ETHTOOL for %s: %m", - ifr.ifr_name); must_write(remote, &rc, sizeof(int)); return; } @@ -338,14 +335,12 @@ asroot_ethtool() } static void -asroot_iface_init() +asroot_iface_eth_init() { struct sockaddr_ll sa; - int un = 1; - int s, master; + int s; char ifname[IFNAMSIZ]; - must_read(remote, &master, sizeof(int)); must_read(remote, ifname, IFNAMSIZ); ifname[IFNAMSIZ-1] = '\0'; @@ -358,27 +353,12 @@ asroot_iface_init() memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = 0; - if (master == -1) - sa.sll_ifindex = if_nametoindex(ifname); - else - sa.sll_ifindex = master; + sa.sll_ifindex = if_nametoindex(ifname); if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) { must_write(remote, &errno, sizeof(errno)); close(s); return; } - - if (master != -1) { - /* With bonding, we need to listen to bond device. We use - * setsockopt() PACKET_ORIGDEV to get physical device instead of - * bond device */ - if (setsockopt(s, SOL_PACKET, - PACKET_ORIGDEV, &un, sizeof(un)) == -1) { - LLOG_WARN("[priv]: unable to setsockopt for master bonding device of %s. " - "You will get inaccurate results", - ifname); - } - } errno = 0; must_write(remote, &errno, sizeof(errno)); send_fd(remote, s); @@ -454,7 +434,7 @@ static struct dispatch_actions actions[] = { {PRIV_GET_HOSTNAME, asroot_gethostbyname}, {PRIV_OPEN, asroot_open}, {PRIV_ETHTOOL, asroot_ethtool}, - {PRIV_IFACE_INIT, asroot_iface_init}, + {PRIV_IFACE_INIT, asroot_iface_eth_init}, {PRIV_IFACE_MULTICAST, asroot_iface_multicast}, {PRIV_SNMP_SOCKET, asroot_snmp_socket}, {-1, NULL} diff --git a/src/sonmp.c b/src/sonmp.c index e58fb989..cb201fb1 100644 --- a/src/sonmp.c +++ b/src/sonmp.c @@ -226,7 +226,7 @@ sonmp_send(struct lldpd *global, /* Segment on three bytes, we don't have slots, so we skip the first two bytes */ POKE_UINT16(0) && - POKE_UINT8(if_nametoindex(hardware->h_ifname)) && + POKE_UINT8(hardware->h_ifindex) && POKE_UINT8(1) && /* Chassis: Other */ POKE_UINT8(12) && /* Back: Ethernet, Fast Ethernet and Gigabit */ POKE_UINT8(SONMP_TOPOLOGY_NEW) && /* Should work. We have no state */ @@ -234,7 +234,8 @@ sonmp_send(struct lldpd *global, POKE_SAVE(end))) goto toobig; - if (write(hardware->h_raw, packet, end - packet) == -1) { + if (hardware->h_ops->send(global, hardware, + (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", hardware->h_ifname); free(packet); @@ -247,7 +248,8 @@ sonmp_send(struct lldpd *global, PEEK_DISCARD(ETH_ALEN - 1); /* Modify the last byte of the MAC address */ POKE_UINT8(1); - if (write(hardware->h_raw, packet, end - packet) == -1) { + if (hardware->h_ops->send(global, hardware, + (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send second SONMP packet on real device for %s", hardware->h_ifname); free(packet); -- 2.39.5