summaryrefslogtreecommitdiffstats
path: root/src/daemon/interfaces.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/daemon/interfaces.c764
1 files changed, 764 insertions, 0 deletions
diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c
new file mode 100644
index 0000000..c1179d9
--- /dev/null
+++ b/src/daemon/interfaces.c
@@ -0,0 +1,764 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+#include "trace.h"
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+static int
+lldpd_af(int af)
+{
+ switch (af) {
+ case LLDPD_AF_IPV4:
+ return AF_INET;
+ case LLDPD_AF_IPV6:
+ return AF_INET6;
+ case LLDPD_AF_LAST:
+ return AF_MAX;
+ default:
+ return AF_UNSPEC;
+ }
+}
+
+/* Generic ethernet interface initialization */
+/**
+ * Enable multicast on the given interface.
+ */
+void
+interfaces_setup_multicast(struct lldpd *cfg, const char *name, int remove)
+{
+ int rc;
+ size_t i, j;
+ const u_int8_t *mac;
+ const u_int8_t zero[ETHER_ADDR_LEN] = {};
+
+ for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
+ if (!cfg->g_protocols[i].enabled) continue;
+ for (j = 0; j < sizeof(cfg->g_protocols[0].mac) /
+ sizeof(cfg->g_protocols[0].mac[0]);
+ j++) {
+ mac = cfg->g_protocols[i].mac[j];
+ if (memcmp(mac, zero, ETHER_ADDR_LEN) == 0) break;
+ if ((rc = priv_iface_multicast(name, mac, !remove)) != 0) {
+ errno = rc;
+ if (errno != ENOENT)
+ log_debug("interfaces",
+ "unable to %s %s address to multicast filter for %s (%s)",
+ (remove) ? "delete" : "add",
+ cfg->g_protocols[i].name, name,
+ strerror(rc));
+ }
+ }
+ }
+}
+
+/**
+ * Free an interface.
+ *
+ * @param iff interface to be freed
+ */
+void
+interfaces_free_device(struct interfaces_device *iff)
+{
+ if (!iff) return;
+ free(iff->name);
+ free(iff->alias);
+ free(iff->address);
+ free(iff->driver);
+ free(iff);
+}
+
+/**
+ * Free a list of interfaces.
+ *
+ * @param ifs list of interfaces to be freed
+ */
+void
+interfaces_free_devices(struct interfaces_device_list *ifs)
+{
+ struct interfaces_device *iff, *iff_next;
+ if (!ifs) return;
+ for (iff = TAILQ_FIRST(ifs); iff != NULL; iff = iff_next) {
+ iff_next = TAILQ_NEXT(iff, next);
+ interfaces_free_device(iff);
+ }
+ free(ifs);
+}
+
+/**
+ * Free one address
+ *
+ * @param ifaddr Address to be freed
+ */
+void
+interfaces_free_address(struct interfaces_address *ifaddr)
+{
+ free(ifaddr);
+}
+
+/**
+ * Free a list of addresses.
+ *
+ * @param ifaddrs list of addresses
+ */
+void
+interfaces_free_addresses(struct interfaces_address_list *ifaddrs)
+{
+ struct interfaces_address *ifa, *ifa_next;
+ if (!ifaddrs) return;
+ for (ifa = TAILQ_FIRST(ifaddrs); ifa != NULL; ifa = ifa_next) {
+ ifa_next = TAILQ_NEXT(ifa, next);
+ interfaces_free_address(ifa);
+ }
+ free(ifaddrs);
+}
+
+/**
+ * Find the appropriate interface from the name.
+ *
+ * @param interfaces List of available interfaces
+ * @param device Name of the device we search for
+ * @return The interface or NULL if not found
+ */
+struct interfaces_device *
+interfaces_nametointerface(struct interfaces_device_list *interfaces,
+ const char *device)
+{
+ struct interfaces_device *iface;
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (!strncmp(iface->name, device, IFNAMSIZ)) return iface;
+ }
+ log_debug("interfaces", "cannot get interface for index %s", device);
+ return NULL;
+}
+
+/**
+ * Find the appropriate interface from the index.
+ *
+ * @param interfaces List of available interfaces
+ * @param index Index of the device we search for
+ * @return The interface or NULL if not found
+ */
+struct interfaces_device *
+interfaces_indextointerface(struct interfaces_device_list *interfaces, int index)
+{
+ struct interfaces_device *iface;
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (iface->index == index) return iface;
+ }
+ log_debug("interfaces", "cannot get interface for index %d", index);
+ return NULL;
+}
+
+void
+interfaces_helper_allowlist(struct lldpd *cfg,
+ struct interfaces_device_list *interfaces)
+{
+ struct interfaces_device *iface;
+
+ if (!cfg->g_config.c_iface_pattern) return;
+
+ TAILQ_FOREACH (iface, interfaces, next) {
+ int m = pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0);
+ switch (m) {
+ case PATTERN_MATCH_DENIED:
+ log_debug("interfaces", "deny %s", iface->name);
+ iface->ignore = 1;
+ continue;
+ case PATTERN_MATCH_ALLOWED_EXACT:
+ log_debug("interfaces",
+ "allow %s (consider it as a physical interface)",
+ iface->name);
+ iface->type |= IFACE_PHYSICAL_T;
+ continue;
+ }
+ }
+}
+
+#ifdef ENABLE_DOT1
+static void
+iface_append_vlan(struct lldpd *cfg, struct interfaces_device *vlan,
+ struct interfaces_device *lower)
+{
+ struct lldpd_hardware *hardware =
+ lldpd_get_hardware(cfg, lower->name, lower->index);
+ struct lldpd_port *port;
+ struct lldpd_vlan *v;
+ char *name = NULL;
+ uint16_t vlan_id;
+
+ if (hardware == NULL) {
+ log_debug("interfaces", "cannot find real interface %s for VLAN %s",
+ lower->name, vlan->name);
+ return;
+ }
+ port = &hardware->h_lport;
+
+ for (int i = 0; (i < VLAN_BITMAP_LEN); i++) {
+ if (vlan->vlan_bmap[i] == 0) continue;
+ for (unsigned bit = 0; bit < 32; bit++) {
+ uint32_t mask = 1L << bit;
+ if (!(vlan->vlan_bmap[i] & mask)) continue;
+ vlan_id = (i * 32) + bit;
+ if (asprintf(&name, "vlan%d", vlan_id) == -1) return;
+
+ /* Check if the VLAN is already here. */
+ TAILQ_FOREACH (v, &port->p_vlans, v_entries)
+ if (strncmp(name, v->v_name, IFNAMSIZ) == 0) {
+ free(name);
+ return;
+ }
+
+ if ((v = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ free(name);
+ return;
+ }
+ v->v_name = name;
+ v->v_vid = vlan_id;
+ if (vlan->pvid) port->p_pvid = vlan->pvid;
+ log_debug("interfaces", "append VLAN %s for %s", v->v_name,
+ hardware->h_ifname);
+ TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries);
+ }
+ }
+}
+
+/**
+ * Append VLAN to the lowest possible interface.
+ *
+ * @param vlan The VLAN interface (used to get VLAN ID).
+ * @param upper The upper interface we are currently examining.
+ * @param depth Depth of the stack (avoid infinite recursion)
+ *
+ * Initially, upper == vlan. This function will be called recursively.
+ */
+static void
+iface_append_vlan_to_lower(struct lldpd *cfg, struct interfaces_device_list *interfaces,
+ struct interfaces_device *vlan, struct interfaces_device *upper, int depth)
+{
+ if (depth > 5) {
+ log_warnx("interfaces",
+ "BUG: maximum depth reached when applying VLAN %s (loop?)",
+ vlan->name);
+ return;
+ }
+ depth++;
+ struct interfaces_device *lower;
+ log_debug("interfaces",
+ "looking to apply VLAN %s to physical interface behind %s", vlan->name,
+ upper->name);
+
+ /* Some bridges managed VLAN internally, skip them. */
+ if (upper->type & IFACE_BRIDGE_VLAN_T) {
+ log_debug("interfaces",
+ "VLAN %s ignored for VLAN-aware bridge interface %s", vlan->name,
+ upper->name);
+ return;
+ }
+
+ /* Easy: check if we have a lower interface. */
+ if (upper->lower) {
+ log_debug("interfaces", "VLAN %s on lower interface %s", vlan->name,
+ upper->name);
+ iface_append_vlan_to_lower(cfg, interfaces, vlan, upper->lower, depth);
+ return;
+ }
+
+ /* Other easy case, we have a physical interface. */
+ if (upper->type & IFACE_PHYSICAL_T) {
+ log_debug("interfaces", "VLAN %s on physical interface %s", vlan->name,
+ upper->name);
+ iface_append_vlan(cfg, vlan, upper);
+ return;
+ }
+
+ /* We can now search for interfaces that have our interface as an upper
+ * interface. */
+ TAILQ_FOREACH (lower, interfaces, next) {
+ if (lower->upper != upper) continue;
+ log_debug("interfaces", "VLAN %s on lower interface %s", vlan->name,
+ upper->name);
+ iface_append_vlan_to_lower(cfg, interfaces, vlan, lower, depth);
+ }
+}
+
+void
+interfaces_helper_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces)
+{
+ struct interfaces_device *iface;
+
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (!(iface->type & IFACE_VLAN_T) && bitmap_isempty(iface->vlan_bmap))
+ continue;
+
+ /* We need to find the physical interfaces of this
+ vlan, through bonds and bridges. */
+ log_debug("interfaces",
+ "search physical interface for VLAN interface %s", iface->name);
+ iface_append_vlan_to_lower(cfg, interfaces, iface, iface, 0);
+ }
+}
+#endif
+
+/* Fill out chassis ID if not already done. Only physical interfaces are
+ * considered. */
+void
+interfaces_helper_chassis(struct lldpd *cfg, struct interfaces_device_list *interfaces)
+{
+ struct interfaces_device *iface;
+ struct lldpd_hardware *hardware;
+ char *name = NULL;
+ static u_int8_t zero_mac[] = { 0, 0, 0, 0, 0, 0 };
+
+ if (!cfg->g_config.c_cap_override) {
+ LOCAL_CHASSIS(cfg)->c_cap_enabled &=
+ ~(LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_STATION);
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (iface->type & IFACE_BRIDGE_T)
+ LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
+ if (iface->type & IFACE_WIRELESS_T)
+ LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
+ }
+ if ((LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_STATION) &&
+ (LOCAL_CHASSIS(cfg)->c_cap_enabled == 0))
+ LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_STATION;
+ }
+
+ /* Do not modify the chassis if it's already set to a MAC address or if
+ * it's set to a local address equal to the user-provided
+ * configuration. */
+ if ((LOCAL_CHASSIS(cfg)->c_id != NULL &&
+ LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR) ||
+ cfg->g_config.c_cid_string != NULL)
+ return; /* We already have one */
+
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (!(iface->type & IFACE_PHYSICAL_T)) continue;
+ if (cfg->g_config.c_cid_pattern &&
+ !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0))
+ continue;
+
+ if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) ==
+ NULL)
+ /* That's odd. Let's skip. */
+ continue;
+ if (memcmp(hardware->h_lladdr, zero_mac, ETHER_ADDR_LEN) == 0)
+ /* All-zero MAC address */
+ continue;
+
+ name = malloc(ETHER_ADDR_LEN);
+ if (!name) {
+ log_warn("interfaces", "not enough memory for chassis ID");
+ return;
+ }
+ free(LOCAL_CHASSIS(cfg)->c_id);
+ memcpy(name, hardware->h_lladdr, ETHER_ADDR_LEN);
+ LOCAL_CHASSIS(cfg)->c_id = name;
+ LOCAL_CHASSIS(cfg)->c_id_len = ETHER_ADDR_LEN;
+ LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ return;
+ }
+}
+
+#undef IN_IS_ADDR_LOOPBACK
+#define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK))
+#undef IN_IS_ADDR_ANY
+#define IN_IS_ADDR_ANY(a) ((a)->s_addr == htonl(INADDR_ANY))
+#undef IN_IS_ADDR_LINKLOCAL
+#define IN_IS_ADDR_LINKLOCAL(a) (((a)->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000))
+#undef IN_IS_ADDR_GLOBAL
+#define IN_IS_ADDR_GLOBAL(a) \
+ (!IN_IS_ADDR_LOOPBACK(a) && !IN_IS_ADDR_ANY(a) && !IN_IS_ADDR_LINKLOCAL(a))
+#undef IN6_IS_ADDR_GLOBAL
+#define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a))
+
+/* Add management addresses for the given family. We only take one of each
+ address family, unless a pattern is provided and is not all negative. For
+ example !*:*,!10.* will only deny addresses. We will pick the first IPv4
+ address not matching 10.*.
+*/
+static int
+interfaces_helper_mgmt_for_af(struct lldpd *cfg, int af,
+ struct interfaces_address_list *addrs, struct interfaces_device_list *interfaces,
+ int global, int allnegative)
+{
+ struct interfaces_address *addr;
+ struct interfaces_device *device;
+ struct lldpd_mgmt *mgmt;
+ char addrstrbuf[INET6_ADDRSTRLEN];
+ int found = 0;
+ union lldpd_address in_addr;
+ size_t in_addr_size;
+
+ TAILQ_FOREACH (addr, addrs, next) {
+ if (addr->address.ss_family != lldpd_af(af)) continue;
+
+ switch (af) {
+ case LLDPD_AF_IPV4:
+ in_addr_size = sizeof(struct in_addr);
+ memcpy(&in_addr,
+ &((struct sockaddr_in *)&addr->address)->sin_addr,
+ in_addr_size);
+ if (global) {
+ if (!IN_IS_ADDR_GLOBAL(&in_addr.inet)) continue;
+ } else {
+ if (!IN_IS_ADDR_LINKLOCAL(&in_addr.inet)) continue;
+ }
+ break;
+ case LLDPD_AF_IPV6:
+ in_addr_size = sizeof(struct in6_addr);
+ memcpy(&in_addr,
+ &((struct sockaddr_in6 *)&addr->address)->sin6_addr,
+ in_addr_size);
+ if (global) {
+ if (!IN6_IS_ADDR_GLOBAL(&in_addr.inet6)) continue;
+ } else {
+ if (!IN6_IS_ADDR_LINKLOCAL(&in_addr.inet6)) continue;
+ }
+ break;
+ default:
+ assert(0);
+ continue;
+ }
+ if (inet_ntop(lldpd_af(af), &in_addr, addrstrbuf, sizeof(addrstrbuf)) ==
+ NULL) {
+ log_warn("interfaces",
+ "unable to convert IP address to a string");
+ continue;
+ }
+ if (cfg->g_config.c_mgmt_pattern == NULL ||
+ /* Match on IP address */
+ pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern,
+ allnegative) ||
+ /* Match on interface name */
+ ((device = interfaces_indextointerface(interfaces, addr->index)) &&
+ pattern_match(device->name, cfg->g_config.c_mgmt_pattern,
+ allnegative))) {
+ mgmt =
+ lldpd_alloc_mgmt(af, &in_addr, in_addr_size, addr->index);
+ if (mgmt == NULL) {
+ assert(errno == ENOMEM); /* anything else is a bug */
+ log_warn("interfaces", "out of memory error");
+ return found;
+ }
+ log_debug("interfaces", "add management address %s",
+ addrstrbuf);
+ TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
+ found = 1;
+
+ /* Don't take additional address if the pattern is all negative.
+ */
+ if (allnegative) break;
+ }
+ }
+ return found;
+}
+
+/* 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
+interfaces_helper_mgmt(struct lldpd *cfg, struct interfaces_address_list *addrs,
+ struct interfaces_device_list *interfaces)
+{
+ int allnegative = 0;
+ int af;
+ const char *pattern = cfg->g_config.c_mgmt_pattern;
+
+ lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg));
+ if (!cfg->g_config.c_mgmt_advertise) return;
+
+ /* Is the pattern provided an actual IP address? */
+ if (pattern && strpbrk(pattern, "!,*?") == NULL) {
+ unsigned char addr[sizeof(struct in6_addr)];
+ size_t addr_size;
+ struct lldpd_mgmt *mgmt;
+ struct interfaces_address *ifaddr;
+
+ for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
+ switch (af) {
+ case LLDPD_AF_IPV4:
+ addr_size = sizeof(struct in_addr);
+ break;
+ case LLDPD_AF_IPV6:
+ addr_size = sizeof(struct in6_addr);
+ break;
+ default:
+ assert(0);
+ }
+ if (inet_pton(lldpd_af(af), pattern, addr) == 1) break;
+ }
+ if (af != LLDPD_AF_LAST) {
+ /* Try to get the index if possible. */
+ TAILQ_FOREACH (ifaddr, addrs, next) {
+ if (ifaddr->address.ss_family != lldpd_af(af)) continue;
+ if (LLDPD_AF_IPV4 == af) {
+ struct sockaddr_in *sa_sin;
+ sa_sin = (struct sockaddr_in *)&ifaddr->address;
+ if (0 ==
+ memcmp(addr, &(sa_sin->sin_addr),
+ addr_size))
+ break;
+ } else if (LLDPD_AF_IPV6 == af) {
+ if (0 ==
+ memcmp(addr,
+ &((struct sockaddr_in6 *)&ifaddr
+ ->address)
+ ->sin6_addr,
+ addr_size))
+ break;
+ }
+ }
+
+ mgmt = lldpd_alloc_mgmt(af, addr, addr_size,
+ ifaddr ? ifaddr->index : 0);
+ if (mgmt == NULL) {
+ log_warn("interfaces", "out of memory error");
+ return;
+ }
+ log_debug("interfaces", "add exact management address %s",
+ pattern);
+ TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
+ return;
+ }
+ /* else: could be an interface name */
+ }
+
+ /* Is the pattern provided all negative? */
+ if (pattern == NULL)
+ allnegative = 1;
+ else if (pattern[0] == '!') {
+ /* If each comma is followed by '!', its an all
+ negative pattern */
+ const char *sep = pattern;
+ while ((sep = strchr(sep, ',')) && (*(++sep) == '!'))
+ ;
+ if (sep == NULL) allnegative = 1;
+ }
+
+ /* Find management addresses */
+ for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
+ (void)(interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 1,
+ allnegative) ||
+ interfaces_helper_mgmt_for_af(cfg, af, addrs, interfaces, 0,
+ allnegative));
+ }
+}
+
+/* Fill up port name and description */
+void
+interfaces_helper_port_name_desc(struct lldpd *cfg, struct lldpd_hardware *hardware,
+ struct interfaces_device *iface)
+{
+ struct lldpd_port *port = &hardware->h_lport;
+
+ /* We need to set the portid to what the client configured.
+ This can be done from the CLI.
+ */
+ int has_alias = (iface->alias != NULL && strlen(iface->alias) != 0 &&
+ strncmp("lldpd: ", iface->alias, 7));
+ int portid_type = cfg->g_config.c_lldp_portid_type;
+ if (portid_type == LLDP_PORTID_SUBTYPE_IFNAME ||
+ (portid_type == LLDP_PORTID_SUBTYPE_UNKNOWN && has_alias) ||
+ (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LOCAL && has_alias)) {
+ if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) {
+ log_debug("interfaces", "use ifname for %s",
+ hardware->h_ifname);
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ port->p_id_len = strlen(hardware->h_ifname);
+ free(port->p_id);
+ if ((port->p_id = calloc(1, port->p_id_len)) == NULL)
+ fatal("interfaces", NULL);
+ memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
+ }
+
+ if (port->p_descr_force == 0) {
+ /* use the actual alias in the port description */
+ log_debug("interfaces", "using alias in description for %s",
+ hardware->h_ifname);
+ free(port->p_descr);
+ if (has_alias) {
+ port->p_descr = strdup(iface->alias);
+ } else {
+ /* We don't have anything else to put here and for CDP
+ * with need something non-NULL */
+ port->p_descr = strdup(hardware->h_ifname);
+ }
+ }
+ } else {
+ if (port->p_id_subtype != LLDP_PORTID_SUBTYPE_LOCAL) {
+ log_debug("interfaces", "use MAC address for %s",
+ hardware->h_ifname);
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
+ free(port->p_id);
+ if ((port->p_id = calloc(1, ETHER_ADDR_LEN)) == NULL)
+ fatal("interfaces", NULL);
+ memcpy(port->p_id, hardware->h_lladdr, ETHER_ADDR_LEN);
+ port->p_id_len = ETHER_ADDR_LEN;
+ }
+
+ if (port->p_descr_force == 0) {
+ /* use the ifname in the port description until alias is set */
+ log_debug("interfaces", "using ifname in description for %s",
+ hardware->h_ifname);
+ free(port->p_descr);
+ port->p_descr = strdup(hardware->h_ifname);
+ }
+ }
+}
+
+void
+interfaces_helper_add_hardware(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+ TRACE(LLDPD_INTERFACES_NEW(hardware->h_ifname));
+ TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
+}
+
+void
+interfaces_helper_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces,
+ struct lldpd_ops *ops, int (*init)(struct lldpd *, struct lldpd_hardware *))
+{
+ struct interfaces_device *iface;
+ struct lldpd_hardware *hardware;
+ int created;
+
+ TAILQ_FOREACH (iface, interfaces, next) {
+ if (!(iface->type & IFACE_PHYSICAL_T)) continue;
+ if (iface->ignore) continue;
+
+ log_debug("interfaces", "%s is an acceptable ethernet device",
+ iface->name);
+ created = 0;
+ if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) ==
+ NULL) {
+ if ((hardware = lldpd_alloc_hardware(cfg, iface->name,
+ iface->index)) == NULL) {
+ log_warnx("interfaces",
+ "Unable to allocate space for %s", iface->name);
+ continue;
+ }
+ created = 1;
+ }
+ if (hardware->h_flags) continue;
+ if (hardware->h_ops != ops || hardware->h_ifindex_changed) {
+ if (!created) {
+ log_debug("interfaces",
+ "interface %s is converted from another type of interface",
+ hardware->h_ifname);
+ if (hardware->h_ops && hardware->h_ops->cleanup) {
+ hardware->h_ops->cleanup(cfg, hardware);
+ levent_hardware_release(hardware);
+ levent_hardware_init(hardware);
+ }
+ }
+ if (init(cfg, hardware) != 0) {
+ log_warnx("interfaces", "unable to initialize %s",
+ hardware->h_ifname);
+ lldpd_hardware_cleanup(cfg, hardware);
+ continue;
+ }
+ hardware->h_ops = ops;
+ hardware->h_mangle =
+ (iface->upper && iface->upper->type & IFACE_BOND_T);
+ }
+ if (created)
+ interfaces_helper_add_hardware(cfg, hardware);
+ else
+ lldpd_port_cleanup(&hardware->h_lport, 0);
+
+ hardware->h_flags = iface->flags; /* Should be non-zero */
+ iface->ignore = 1; /* Future handlers
+ don't have to
+ care about this
+ interface. */
+
+ /* Get local address */
+ memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
+
+ /* Fill information about port */
+ interfaces_helper_port_name_desc(cfg, hardware, iface);
+
+ /* Fill additional info */
+ hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
+
+#ifdef ENABLE_DOT3
+ if (iface->upper && iface->upper->type & IFACE_BOND_T)
+ hardware->h_lport.p_aggregid = iface->upper->index;
+ else
+ hardware->h_lport.p_aggregid = 0;
+#endif
+ }
+}
+
+void
+interfaces_helper_promisc(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+ if (!cfg->g_config.c_promisc) return;
+ if (priv_iface_promisc(hardware->h_ifname) != 0) {
+ log_warnx("interfaces", "unable to enable promiscuous mode for %s",
+ hardware->h_ifname);
+ }
+}
+
+/**
+ * Send the packet using the hardware function. Optionnaly mangle the MAC address.
+ *
+ * With bonds, we have duplicate MAC address on different physical
+ * interfaces. We need to alter the source MAC address when we send on an
+ * inactive slave. The `h_mangle` flah is used to know if we need to do
+ * something like that.
+ */
+int
+interfaces_send_helper(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer,
+ size_t size)
+{
+ if (size < 2 * ETHER_ADDR_LEN) {
+ log_warnx("interfaces", "packet to send on %s is too small!",
+ hardware->h_ifname);
+ return 0;
+ }
+ if (hardware->h_mangle) {
+#define MAC_UL_ADMINISTERED_BIT_MASK 0x02
+ char *src_mac = buffer + ETHER_ADDR_LEN;
+ char arbitrary[] = { 0x00, 0x60, 0x08, 0x69, 0x97, 0xef };
+
+ switch (cfg->g_config.c_bond_slave_src_mac_type) {
+ case LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED:
+ if (!(*src_mac & MAC_UL_ADMINISTERED_BIT_MASK)) {
+ *src_mac |= MAC_UL_ADMINISTERED_BIT_MASK;
+ break;
+ }
+ /* Fallback to fixed value */
+ memcpy(src_mac, arbitrary, ETHER_ADDR_LEN);
+ break;
+ case LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED:
+ memcpy(src_mac, arbitrary, ETHER_ADDR_LEN);
+ break;
+ case LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO:
+ memset(src_mac, 0, ETHER_ADDR_LEN);
+ break;
+ }
+ }
+ return hardware->h_ops->send(cfg, hardware, buffer, size);
+}