Move interface related stuff into interfaces.c.
A set of handlers are called sequentially to handle interfaces.
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@
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)
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))
(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) {
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)
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)
(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,
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);
--- /dev/null
+/*
+ * Copyright (c) 2009 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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 <unistd.h>
+
+#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
if (deviceslot[i] == NULL) {
if (!(
POKE_UINT16(8) &&
- POKE_UINT16(if_nametoindex(hardware->h_ifname))))
+ POKE_UINT16(hardware->h_ifindex)))
goto toobig;
}
if (!(
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);
+++ /dev/null
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * 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 <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <net/if_arp.h>
-#include <linux/if_vlan.h>
-#include <linux/if_bonding.h>
-#include <linux/if_bridge.h>
-#include <linux/wireless.h>
-#include <linux/sockios.h>
-
-#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
--- /dev/null
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <net/if_arp.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bonding.h>
+#include <linux/if_bridge.h>
+#include <linux/wireless.h>
+#include <linux/sockios.h>
+#include <linux/filter.h>
+#include <linux/if_vlan.h>
+#include <linux/if_packet.h>
+
+#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);
+ }
+ }
+ }
+ }
+}
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);
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <fnmatch.h>
#include <time.h>
#include <libgen.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
-#include <ifaddrs.h>
#include <net/if_arp.h>
-#include <linux/filter.h>
-#include <linux/if_vlan.h>
-#include <linux/if_packet.h>
-#include <linux/sockios.h>
#ifdef USE_SNMP
#include <net-snmp/net-snmp-config.h>
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,
{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();
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
}
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;
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)
{
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;
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);
}
#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)
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++) {
#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");
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 */
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);
}
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)
#include <arpa/inet.h>
#include <linux/if.h>
#endif
+#include <ifaddrs.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <linux/ethtool.h>
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;
#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
TAILQ_HEAD(, lldpd_hardware) g_hardware;
};
+typedef void(*lldpd_ifhandlers)(struct lldpd *, struct ifaddrs *);
+
enum hmsg_type {
HMSG_NONE,
HMSG_GET_INTERFACES,
#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
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();
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 *);
}
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;
}
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;
}
}
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';
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);
{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}
/* 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 */
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);
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);