/*
* Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
*
- * Permission to use, copy, modify, and distribute this software for any
+ * 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.
*
#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/types.h>
+#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/select.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>
+
+#if LLDPD_FD_SETSIZE != FD_SETSIZE
+# warning "FD_SETSIZE is set to an inconsistent value."
+#endif
#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,
+ { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
LLDP_MULTICAST_ADDR },
#ifdef ENABLE_CDP
{ LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
{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();
+static void lldpd_exit(void);
static void lldpd_send_all(struct lldpd *);
static void lldpd_recv_all(struct lldpd *);
+static void lldpd_hide_all(struct lldpd *);
+static void lldpd_hide_ports(struct lldpd *, struct lldpd_hardware *, int);
static int lldpd_guess_type(struct lldpd *, char *, int);
static void lldpd_decode(struct lldpd *, char *, int,
struct lldpd_hardware *);
+static void lldpd_update_chassis(struct lldpd_chassis *,
+ const struct lldpd_chassis *);
+static char *lldpd_get_lsb_release(void);
+static char *lldpd_get_os_release(void);
#ifdef ENABLE_LLDPMED
static void lldpd_med(struct lldpd_chassis *);
#endif
static char **saved_argv;
+#ifdef HAVE___PROGNAME
+extern const char *__progname;
+#else
+# define __progname "lldpd"
+#endif
static void
usage(void)
{
- extern const char *__progname;
- fprintf(stderr, "usage: %s [options]\n", __progname);
- fprintf(stderr, "see manual page lldpd(8) for more information\n");
- exit(1);
-}
+ fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
-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;
-}
+ fprintf(stderr, "\n");
-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;
- }
+ fprintf(stderr, "-d Do not daemonize.\n");
+ fprintf(stderr, "-r Receive-only mode\n");
+ fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
+ fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
+ fprintf(stderr, "-S descr Override the default system description.\n");
+ fprintf(stderr, "-m IP Specify the management address of this system.\n");
+ fprintf(stderr, "-H mode Specify the behaviour when detecting multiple neighbors.\n");
+#ifdef ENABLE_LLDPMED
+ fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
+ fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
+ fprintf(stderr, " 2 Media Endpoint (Class II)\n");
+ fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
+ fprintf(stderr, " 4 Network Connectivity Device\n");
+#endif
+#ifdef USE_SNMP
+ fprintf(stderr, "-x Enable SNMP subagent.\n");
+#endif
+ fprintf(stderr, "\n");
- lldpd_iface_multicast(global, hardware->h_ifname, 0);
+#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
+ fprintf(stderr, "Additional protocol support.\n");
+#ifdef ENABLE_CDP
+ fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
+#endif
+#ifdef ENABLE_EDP
+ fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
+#endif
+#ifdef ENABLE_FDP
+ fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
+#endif
+#ifdef ENABLE_SONMP
+ fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
+#endif
- LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
- hardware->h_raw);
- return 0;
+ fprintf(stderr, "\n");
+#endif
+
+ fprintf(stderr, "see manual page lldpd(8) for more information\n");
+ exit(1);
}
-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 index, struct lldpd_ops *ops)
{
- 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) &&
+ (hardware->h_ifindex == index) &&
+ ((!ops) || (ops == hardware->h_ops)))
+ 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);
+ hardware->h_lport.p_chassis->c_refcount++;
+ 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
#endif
/* If `all' is true, clear all information, including information that
- are not refreshed periodically. If `all' is true, also free the
- port. */
+ are not refreshed periodically. Port should be freed manually. */
void
-lldpd_port_cleanup(struct lldpd_port *port, int all)
+lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
{
#ifdef ENABLE_LLDPMED
int i;
free(port->p_descr);
if (all) {
free(port->p_lastframe);
- if (port->p_chassis) /* chassis may not have been attributed, yet */
+ if (port->p_chassis) { /* chassis may not have been attributed, yet */
port->p_chassis->c_refcount--;
- free(port);
+ port->p_chassis = NULL;
+ }
}
}
}
if (del) {
TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
- lldpd_port_cleanup(port, 1);
+ lldpd_port_cleanup(cfg, port, 1);
+ free(port);
}
}
}
void
-lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
+lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
{
- lldpd_port_cleanup(&hardware->h_lport, 1);
+ int i;
+ lldpd_port_cleanup(cfg, &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 < LLDPD_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;
+ struct lldpd_chassis *chassis, *chassis_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);
+ for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
+ chassis = chassis_next) {
+ chassis_next = TAILQ_NEXT(chassis, c_entries);
+ if (chassis->c_refcount == 0) {
+ TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
+ lldpd_chassis_cleanup(chassis, 1);
}
-
- TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
}
-
- return (hardware);
}
static int
struct lldpd_port *port, *oport = NULL;
int guess = LLDPD_MODE_LLDP;
- /* Discard VLAN frames */
- if ((s >= sizeof(struct ethhdr)) &&
- (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
+ if (s < sizeof(struct ethhdr) + 4)
+ /* Too short, just discard it */
return;
+ /* Decapsulate VLAN frames */
+ if (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)) {
+ /* VLAN decapsulation means to shift 4 bytes left the frame from
+ * offset 2*ETH_ALEN */
+ memmove(frame + 2*ETH_ALEN, frame + 2*ETH_ALEN + 4, s - 2*ETH_ALEN);
+ s -= 4;
+ }
TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
if ((oport->p_lastframe != NULL) &&
if (oport) {
/* The port is known, remove it before adding it back */
TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
- lldpd_port_cleanup(oport, 1);
+ lldpd_port_cleanup(cfg, oport, 1);
+ free(oport);
}
if (ochassis) {
- /* Chassis is known, replace values. Hackish */
- chassis->c_refcount = ochassis->c_refcount;
- chassis->c_index = ochassis->c_index;
- memcpy(&chassis->c_entries, &ochassis->c_entries,
- sizeof(chassis->c_entries));
- lldpd_chassis_cleanup(ochassis, 0);
- memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
+ lldpd_update_chassis(ochassis, chassis);
free(chassis);
chassis = ochassis;
} else {
/* Chassis not known, add it */
chassis->c_index = ++cfg->g_lastrid;
- port->p_chassis = chassis;
chassis->c_refcount = 0;
TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
port->p_chassis = chassis;
port->p_chassis->c_refcount++;
- i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
- LLOG_DEBUG("Currently, %s known %d neighbors",
+ /* Several cases are possible :
+ 1. chassis is new, its refcount was 0. It is now attached
+ to this port, its refcount is 1.
+ 2. chassis already exists and was attached to another
+ port, we increase its refcount accordingly.
+ 3. chassis already exists and was attached to the same
+ port, its refcount was decreased with
+ lldpd_port_cleanup() and is now increased again.
+
+ In all cases, if the port already existed, it has been
+ freed with lldpd_port_cleanup() and therefore, the refcount
+ of the chassis that was attached to it is decreased.
+ */
+ i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries)
+ i++;
+ LLOG_DEBUG("Currently, %s knows %d neighbors",
hardware->h_ifname, i);
return;
}
+/* Update chassis `ochassis' with values from `chassis'. */
+static void
+lldpd_update_chassis(struct lldpd_chassis *ochassis,
+ const struct lldpd_chassis *chassis) {
+ TAILQ_ENTRY(lldpd_chassis) entries;
+ /* We want to keep refcount, index and list stuff from the current
+ * chassis */
+ int refcount = ochassis->c_refcount;
+ int index = ochassis->c_index;
+ memcpy(&entries, &ochassis->c_entries,
+ sizeof(entries));
+ /* Make the copy */
+ lldpd_chassis_cleanup(ochassis, 0);
+ memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
+ /* Restore saved values */
+ ochassis->c_refcount = refcount;
+ ochassis->c_index = index;
+ memcpy(&ochassis->c_entries, &entries, sizeof(entries));
+}
+
+/* Get the output of lsb_release -s -d. This is a slow function. It should be
+ called once. It return NULL if any problem happens. Otherwise, this is a
+ statically allocated buffer. The result includes the trailing \n */
+static char *
+lldpd_get_lsb_release() {
+ static char release[1024];
+ char *const command[] = { "lsb_release", "-s", "-d", NULL };
+ int pid, status, devnull, count;
+ int pipefd[2];
+
+ if (pipe(pipefd)) {
+ LLOG_WARN("unable to get a pair of pipes");
+ return NULL;
+ }
+
+ if ((pid = fork()) < 0) {
+ LLOG_WARN("unable to fork");
+ return NULL;
+ }
+ switch (pid) {
+ case 0:
+ /* Child, exec lsb_release */
+ close(pipefd[0]);
+ if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
+ dup2(devnull, STDIN_FILENO);
+ dup2(devnull, STDERR_FILENO);
+ dup2(pipefd[1], STDOUT_FILENO);
+ if (devnull > 2) close(devnull);
+ if (pipefd[1] > 2) close(pipefd[1]);
+ execvp("lsb_release", command);
+ }
+ exit(127);
+ break;
+ default:
+ /* Father, read the output from the children */
+ close(pipefd[1]);
+ count = 0;
+ do {
+ status = read(pipefd[0], release+count, sizeof(release)-count);
+ if ((status == -1) && (errno == EINTR)) continue;
+ if (status > 0)
+ count += status;
+ } while (count < sizeof(release) && (status > 0));
+ if (status < 0) {
+ LLOG_WARN("unable to read from lsb_release");
+ close(pipefd[0]);
+ waitpid(pid, &status, 0);
+ return NULL;
+ }
+ close(pipefd[0]);
+ if (count >= sizeof(release)) {
+ LLOG_INFO("output of lsb_release is too large");
+ waitpid(pid, &status, 0);
+ return NULL;
+ }
+ status = -1;
+ if (waitpid(pid, &status, 0) != pid)
+ return NULL;
+ if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
+ LLOG_INFO("lsb_release information not available");
+ return NULL;
+ }
+ if (!count) {
+ LLOG_INFO("lsb_release returned an empty string");
+ return NULL;
+ }
+ release[count] = '\0';
+ return release;
+ }
+ /* Should not be here */
+ return NULL;
+}
+
+/* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */
+static char *
+lldpd_get_os_release() {
+ static char release[1024];
+
+ FILE *fp = fopen("/etc/os-release", "r");
+ if (!fp) {
+ LLOG_WARN("Could not open /etc/os-release to read system information");
+ return NULL;
+ }
+
+ char line[1024];
+ char *key, *val;
+
+ while ((fgets(line, 1024, fp) != NULL)) {
+ key = strtok(line, "=");
+ val = strtok(NULL, "=");
+
+ if (strncmp(key, "PRETTY_NAME", 1024) == 0) {
+ strncpy(release, val, 1024);
+ break;
+ }
+ }
+ fclose(fp);
+
+ /* Remove trailing newline and all " in the string. */
+ char *ptr1 = release;
+ char *ptr2 = release;
+ while (*ptr1 != 0) {
+ if ((*ptr1 == '"') || (*ptr1 == '\n')) {
+ ++ptr1;
+ } else {
+ *ptr2++ = *ptr1++;
+ }
+ }
+ *ptr2 = 0;
+
+ return release;
+}
+
+int
+lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
+{
+ struct lldpd_callback *callback;
+ if ((callback = (struct lldpd_callback *)
+ malloc(sizeof(struct lldpd_callback))) == NULL)
+ return -1;
+ callback->fd = fd;
+ callback->function = fn;
+ callback->data = data;
+ TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
+ return 0;
+}
+
+void
+lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
+{
+ struct lldpd_callback *callback, *callback_next;
+ for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+ callback;
+ callback = callback_next) {
+ callback_next = TAILQ_NEXT(callback, next);
+ if ((callback->fd == fd) &&
+ (callback->function = fn)) {
+ free(callback->data);
+ TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
+ free(callback);
+ }
+ }
+}
+
+/* Hide unwanted ports depending on smart mode set by the user */
+static void
+lldpd_hide_all(struct lldpd *cfg)
+{
+ struct lldpd_hardware *hardware;
+
+ if (!cfg->g_smart)
+ return;
+ TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+ if (cfg->g_smart & SMART_INCOMING_FILTER)
+ lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
+ if (cfg->g_smart & SMART_OUTGOING_FILTER)
+ lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
+ }
+}
+
+static void
+lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
+ struct lldpd_port *port;
+ int protocols[LLDPD_MODE_MAX+1];
+ char buffer[256];
+ int i, j, k, found;
+ unsigned int min;
+
+ /* Compute the number of occurrences of each protocol */
+ for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
+ TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+ protocols[port->p_protocol]++;
+
+ /* Turn the protocols[] array into an array of
+ enabled/disabled protocols. 1 means enabled, 0
+ means disabled. */
+ min = (unsigned int)-1;
+ for (i = 0; i <= LLDPD_MODE_MAX; i++)
+ if (protocols[i] && (protocols[i] < min))
+ min = protocols[i];
+ found = 0;
+ for (i = 0; i <= LLDPD_MODE_MAX; i++)
+ if ((protocols[i] == min) && !found) {
+ /* If we need a tie breaker, we take
+ the first protocol only */
+ if (cfg->g_smart & mask &
+ (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
+ found = 1;
+ protocols[i] = 1;
+ } else protocols[i] = 0;
+
+ /* We set the p_hidden flag to 1 if the protocol is disabled */
+ TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+ if (mask == SMART_OUTGOING)
+ port->p_hidden_out = protocols[port->p_protocol]?0:1;
+ else
+ port->p_hidden_in = protocols[port->p_protocol]?0:1;
+ }
+
+ /* If we want only one neighbor, we take the first one */
+ if (cfg->g_smart & mask &
+ (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
+ found = 0;
+ TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+ if (mask == SMART_OUTGOING) {
+ if (found) port->p_hidden_out = 1;
+ if (!port->p_hidden_out)
+ found = 1;
+ }
+ if (mask == SMART_INCOMING) {
+ if (found) port->p_hidden_in = 1;
+ if (!port->p_hidden_in)
+ found = 1;
+ }
+ }
+ }
+
+ /* Print a debug message summarizing the operation */
+ for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
+ k = j = 0;
+ TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+ if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
+ ((mask == SMART_INCOMING) && port->p_hidden_in))) {
+ k++;
+ protocols[port->p_protocol] = 1;
+ }
+ j++;
+ }
+ buffer[0] = '\0';
+ for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+ if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) {
+ if (strlen(buffer) +
+ strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
+ /* Unlikely, our buffer is too small */
+ memcpy(buffer + sizeof(buffer) - 4, "...", 4);
+ break;
+ }
+ if (buffer[0])
+ strcat(buffer, ", ");
+ strcat(buffer, cfg->g_protocols[i].name);
+ }
+ }
+ LLOG_DEBUG("[%s] %s: %d visible neigh / %d. Protocols: %s.",
+ (mask == SMART_OUTGOING)?"out filter":"in filter",
+ hardware->h_ifname, k, j, buffer[0]?buffer:"(none)");
+}
+
static void
lldpd_recv_all(struct lldpd *cfg)
{
struct lldpd_hardware *hardware;
- struct lldpd_client *client, *client_next;
+ struct lldpd_callback *callback, *callback_next;
fd_set rfds;
struct timeval tv;
- struct sockaddr_ll from;
- socklen_t fromlen;
#ifdef USE_SNMP
- int fakeblock = 0;
- struct timeval *tvp = &tv;
+ struct timeval snmptv;
+ int snmpblock = 0;
#endif
int rc, nfds, n;
char *buffer;
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 < LLDPD_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);
- if (nfds < client->fd)
- nfds = client->fd;
+ TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
+ FD_SET(callback->fd, &rfds);
+ if (nfds < callback->fd)
+ nfds = callback->fd;
}
- FD_SET(cfg->g_ctl, &rfds);
- if (nfds < cfg->g_ctl)
- nfds = cfg->g_ctl;
#ifdef USE_SNMP
- if (cfg->g_snmp)
- snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
+ if (cfg->g_snmp) {
+ snmpblock = 0;
+ memcpy(&snmptv, &tv, sizeof(struct timeval));
+ snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock);
+ if (snmpblock == 0)
+ memcpy(&tv, &snmptv, sizeof(struct timeval));
+ }
#endif /* USE_SNMP */
if (nfds == -1) {
sleep(cfg->g_delay);
}
#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);
- free(buffer);
+ for (n = 0; n < LLDPD_FD_SETSIZE; n++)
+ if ((FD_ISSET(n, &hardware->h_recvfds)) &&
+ (FD_ISSET(n, &rfds))) break;
+ if (n == LLDPD_FD_SETSIZE) continue;
+ if ((buffer = (char *)malloc(
+ hardware->h_mtu)) == NULL) {
+ LLOG_WARN("failed to alloc reception buffer");
+ continue;
}
-
- }
- if (FD_ISSET(cfg->g_ctl, &rfds)) {
- if (ctl_accept(cfg, cfg->g_ctl) == -1)
- LLOG_WARN("unable to accept new client");
- }
- for (client = TAILQ_FIRST(&cfg->g_clients);
- client != NULL;
- client = client_next) {
- client_next = TAILQ_NEXT(client, next);
- if (FD_ISSET(client->fd, &rfds)) {
- /* Got a message */
- if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
- NULL) {
- LLOG_WARN("failed to alloc reception buffer");
- continue;
- }
- if ((n = recv(client->fd, buffer,
- MAX_HMSGSIZE, 0)) == -1) {
- LLOG_WARN("error while receiving message");
- free(buffer);
- continue;
- }
- if (n > 0)
- client_handle_client(cfg, client, buffer, n);
- else
- ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
+ 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);
+ lldpd_hide_all(cfg); /* Immediatly hide */
+ free(buffer);
+ break;
+ }
+ for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+ callback;
+ callback = callback_next) {
+ /* Callback function can use TAILQ_REMOVE */
+ callback_next = TAILQ_NEXT(callback, next);
+ if (FD_ISSET(callback->fd, &rfds))
+ callback->function(cfg, callback);
}
#ifdef USE_SNMP
{
struct lldpd_hardware *hardware;
struct lldpd_port *port;
- int i, sent = 0;
+ int i, sent;
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;
+ sent = 0;
for (i=0; cfg->g_protocols[i].mode != 0; i++) {
if (!cfg->g_protocols[i].enabled)
continue;
/* We send only if we have at least one remote system
- * speaking this protocol */
+ * speaking this protocol or if the protocol is forced */
+ if (cfg->g_protocols[i].enabled > 1) {
+ cfg->g_protocols[i].send(cfg, hardware);
+ sent++;
+ continue;
+ }
TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+ /* If this remote port is disabled, we don't
+ * consider it */
+ if (port->p_hidden_out &&
+ (cfg->g_smart & SMART_OUTGOING_FILTER))
+ continue;
if (port->p_protocol ==
cfg->g_protocols[i].mode) {
cfg->g_protocols[i].send(cfg,
hardware);
- sent = 1;
+ sent++;
break;
}
}
#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");
free(LOCAL_CHASSIS(cfg)->c_descr);
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)
- fatal("failed to set system description");
+ if (cfg->g_descr_override) {
+ if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
+ cfg->g_descr_override) == -1)
+ fatal("failed to set full system description");
+ } else {
+ if (cfg->g_advertise_version) {
+ if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
+ cfg->g_lsb_release?cfg->g_lsb_release:"",
+ un.sysname, un.release, un.machine)
+ == -1)
+ fatal("failed to set full system description");
+ } else {
+ if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
+ cfg->g_lsb_release?cfg->g_lsb_release:un.sysname) == -1)
+ fatal("failed to set minimal system description");
+ }
+ }
/* Check forwarding */
- LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
- if ((read(f, &status, 1) == 1) && (status == '1')) {
- LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
- }
+ if ((read(f, &status, 1) == 1) && (status == '1'))
+ LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER;
+ else
+ LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER;
close(f);
}
#ifdef ENABLE_LLDPMED
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);
+ if (cfg->g_advertise_version)
+ LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
+ else
+ LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
#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_bond, /* Handle bond */
+ lldpd_ifh_eth, /* Handle classic ethernet interfaces */
+#ifdef ENABLE_DOT1
+ lldpd_ifh_vlan, /* Handle VLAN */
+#endif
+ 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
+ 6. Update smart mode
+ */
+ LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
+ lldpd_update_localports(cfg);
lldpd_cleanup(cfg);
-
- lldpd_send_all(cfg);
+ lldpd_update_localchassis(cfg);
+ if (!cfg->g_receiveonly)
+ 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)
#endif /* USE_SNMP */
}
+struct intint { int a; int b; };
+static const struct intint filters[] = {
+ { 0, 0 },
+ { 1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+ { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
+ { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+ { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
+ { 5, SMART_INCOMING_FILTER },
+ { 6, SMART_OUTGOING_FILTER },
+ { 7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+ { 8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH },
+ { 9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+ { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+ { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
+ { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+ { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER },
+ { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+ { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+ SMART_OUTGOING_FILTER },
+ { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+ { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+ SMART_OUTGOING_FILTER },
+ { 18, SMART_INCOMING_FILTER |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+ { 19, SMART_INCOMING_FILTER |
+ SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+ { -1, 0 }
+};
+
int
-main(int argc, char *argv[])
+lldpd_main(int argc, char *argv[])
{
struct lldpd *cfg;
struct lldpd_chassis *lchassis;
int ch, debug = 0;
#ifdef USE_SNMP
int snmp = 0;
+ char *agentx = NULL; /* AgentX socket */
#endif
char *mgmtp = NULL;
- char *popt, opts[] = "dxm:p:M:i@ ";
- int i, found;
+ char *popt, opts[] =
+ "H:hkrdxX:m:p:M:S:i@ ";
+ int i, found, advertise_version = 1;
#ifdef ENABLE_LLDPMED
int lldpmed = 0, noinventory = 0;
#endif
+ char *descr_override = NULL;
+ char *lsb_release = NULL;
+ int smart = 15;
+ int receiveonly = 0;
saved_argv = argv;
/*
* Get and parse command line options
*/
- popt = index(opts, '@');
- for (i=0; protos[i].mode != 0; i++) {
- if (protos[i].enabled == 1) continue;
+ popt = strchr(opts, '@');
+ for (i=0; protos[i].mode != 0; i++)
*(popt++) = protos[i].arg;
- }
*popt = '\0';
while ((ch = getopt(argc, argv, opts)) != -1) {
switch (ch) {
+ case 'h':
+ usage();
+ break;
case 'd':
debug++;
break;
+ case 'r':
+ receiveonly = 1;
+ break;
case 'm':
mgmtp = optarg;
break;
+ case 'k':
+ advertise_version = 0;
+ break;
#ifdef ENABLE_LLDPMED
case 'M':
lldpmed = atoi(optarg);
usage();
break;
#endif
- case 'x':
#ifdef USE_SNMP
+ case 'x':
snmp = 1;
+ break;
+ case 'X':
+ snmp = 1;
+ agentx = optarg;
+ break;
#else
+ case 'x':
+ case 'X':
fprintf(stderr, "SNMP support is not built-in\n");
usage();
#endif
break;
+ case 'S':
+ descr_override = strdup(optarg);
+ break;
+ case 'H':
+ smart = atoi(optarg);
+ break;
default:
found = 0;
for (i=0; protos[i].mode != 0; i++) {
- if (protos[i].enabled) continue;
if (ch == protos[i].arg) {
- protos[i].enabled = 1;
+ protos[i].enabled++;
+ /* When an argument enable
+ several protocols, only the
+ first one can be forced. */
+ if (found && protos[i].enabled > 1)
+ protos[i].enabled = 1;
found = 1;
}
}
usage();
}
}
+
+ /* Set correct smart mode */
+ for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
+ if (filters[i].a == -1) {
+ fprintf(stderr, "Incorrect mode for -H\n");
+ usage();
+ }
+ smart = filters[i].b;
- log_init(debug);
+ log_init(debug, __progname);
+ tzset(); /* Get timezone info before chroot */
if (!debug) {
int pid;
close(pid);
}
+ /* Try to read system information from /etc/os-release if possible.
+ Fall back to lsb_release for compatibility. */
+ lsb_release = lldpd_get_os_release();
+ if (!lsb_release) {
+ lsb_release = lldpd_get_lsb_release();
+ }
+
priv_init(PRIVSEP_CHROOT);
if ((cfg = (struct lldpd *)
fatal(NULL);
cfg->g_mgmt_pattern = mgmtp;
+ cfg->g_smart = smart;
+ cfg->g_receiveonly = receiveonly;
/* Get ioctl socket */
if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
fatal("failed to get ioctl socket");
cfg->g_delay = LLDPD_TX_DELAY;
+ /* Description */
+ if (!(cfg->g_advertise_version = advertise_version))
+ /* Remove the \n */
+ lsb_release[strlen(lsb_release) - 1] = '\0';
+ cfg->g_lsb_release = lsb_release;
+ if (descr_override)
+ cfg->g_descr_override = descr_override;
+
/* Set system capabilities */
if ((lchassis = (struct lldpd_chassis*)
calloc(1, sizeof(struct lldpd_chassis))) == NULL)
lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
lchassis->c_med_type = lldpmed;
lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
- LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
+ LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
+ LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
cfg->g_noinventory = noinventory;
} else
cfg->g_noinventory = 1;
cfg->g_protocols = protos;
for (i=0; protos[i].mode != 0; i++)
- if (protos[i].enabled) {
+ if (protos[i].enabled > 1)
+ LLOG_INFO("protocol %s enabled and forced", protos[i].name);
+ else if (protos[i].enabled)
LLOG_INFO("protocol %s enabled", protos[i].name);
- } else
+ else
LLOG_INFO("protocol %s disabled", protos[i].name);
TAILQ_INIT(&cfg->g_hardware);
TAILQ_INIT(&cfg->g_chassis);
TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
- lchassis->c_refcount++;
+ lchassis->c_refcount++; /* We should always keep a reference to local chassis */
+
+ TAILQ_INIT(&cfg->g_callbacks);
#ifdef USE_SNMP
if (snmp) {
cfg->g_snmp = 1;
- agent_init(cfg, debug);
+ agent_init(cfg, agentx, debug);
}
#endif /* USE_SNMP */
/* Create socket */
- if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
+ if ((cfg->g_ctl = priv_ctl_create()) == -1)
fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
- TAILQ_INIT(&cfg->g_clients);
+ if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
+ fatalx("unable to add callback for control socket");
gcfg = cfg;
if (atexit(lldpd_exit) != 0) {