summaryrefslogtreecommitdiffstats
path: root/src/daemon/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/protocols')
-rw-r--r--src/daemon/protocols/cdp.c711
-rw-r--r--src/daemon/protocols/cdp.h70
-rw-r--r--src/daemon/protocols/edp.c514
-rw-r--r--src/daemon/protocols/edp.h41
-rw-r--r--src/daemon/protocols/lldp.c1315
-rw-r--r--src/daemon/protocols/sonmp.c410
-rw-r--r--src/daemon/protocols/sonmp.h42
7 files changed, 3103 insertions, 0 deletions
diff --git a/src/daemon/protocols/cdp.c b/src/daemon/protocols/cdp.c
new file mode 100644
index 0000000..620455b
--- /dev/null
+++ b/src/daemon/protocols/cdp.c
@@ -0,0 +1,711 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/* We also supports FDP which is very similar to CDPv1 */
+#include "../lldpd.h"
+#include "../frame.h"
+
+/*
+ * CDP Requests Power at the switch output and therefore has to take into
+ * account the loss in the PoE cable. This is done by the switch automatically
+ * if lldp is used as the protocol.
+ */
+#define CDP_CLASS_3_MAX_PSE_POE 154 /* 15.4W Max PoE at PSE class 3 */
+#define CDP_SWTICH_DEFAULT_POE_PD 130 /* 13.W default PoE at PD */
+#define CDP_SWTICH_DEFAULT_POE_PSE 154 /* 15.4W default PoE at PSE */
+#define CDP_SWITCH_POE_CLASS_4_OFFSET 45 /* 4.5W max loss from cable */
+#define CDP_SWITCH_POE_CLASS_3_OFFSET 24 /* 2.4W max loss from cable */
+
+#if defined ENABLE_CDP || defined ENABLE_FDP
+
+# include <stdio.h>
+# include <unistd.h>
+# include <errno.h>
+# include <arpa/inet.h>
+
+static int
+cdp_send(struct lldpd *global, struct lldpd_hardware *hardware, int version)
+{
+ const char *platform = "Unknown";
+ struct lldpd_chassis *chassis;
+ struct lldpd_mgmt *mgmt;
+ struct lldpd_port *port;
+ u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
+ u_int8_t llcorg[] = LLC_ORG_CISCO;
+# ifdef ENABLE_FDP
+ const char *capstr;
+# endif
+ u_int16_t checksum;
+ int length, i;
+ u_int32_t cap;
+ u_int8_t *packet;
+ u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;
+
+ log_debug("cdp", "send CDP frame on %s", hardware->h_ifname);
+
+ port = &(hardware->h_lport);
+ chassis = port->p_chassis;
+
+# ifdef ENABLE_FDP
+ if (version == 0) {
+ /* With FDP, change multicast address and LLC PID */
+ const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR;
+ const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY;
+ memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr));
+ memcpy(llcorg, fdpllcorg, sizeof(llcorg));
+ }
+# endif
+
+ length = hardware->h_mtu;
+ if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM;
+ pos = packet;
+
+ /* Ethernet header */
+ if (!(POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+ POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
+ POKE_SAVE(pos_len_eh) && /* We compute the len later */
+ POKE_UINT16(0)))
+ goto toobig;
+
+ /* LLC */
+ if (!(POKE_SAVE(pos_llc) && POKE_UINT8(0xaa) && /* SSAP */
+ POKE_UINT8(0xaa) && /* DSAP */
+ POKE_UINT8(0x03) && /* Control field */
+ POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_CDP)))
+ goto toobig;
+
+ /* CDP header */
+ if (!(POKE_SAVE(pos_cdp) && POKE_UINT8((version == 0) ? 1 : version) &&
+ POKE_UINT8(global ? global->g_config.c_ttl : 180) &&
+ POKE_SAVE(pos_checksum) && /* Save checksum position */
+ POKE_UINT16(0)))
+ goto toobig;
+
+ /* Chassis ID */
+ const char *chassis_name = chassis->c_name ? chassis->c_name : "";
+ if (!(POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
+ POKE_BYTES(chassis_name, strlen(chassis_name)) && POKE_END_CDP_TLV))
+ goto toobig;
+
+ /* Adresses */
+ /* See:
+ * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
+ *
+ * It seems that Cisco implies that CDP supports IPv6 using
+ * 802.2 address format with 0xAAAA03 0x000000 0x0800, but
+ * 0x0800 is the Ethernet protocol type for IPv4. Therefore,
+ * we support only IPv4. */
+ i = 0;
+ TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries)
+ if (mgmt->m_family == LLDPD_AF_IPV4) i++;
+ if (i > 0) {
+ if (!(POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && POKE_UINT32(i)))
+ goto toobig;
+ TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) {
+ switch (mgmt->m_family) {
+ case LLDPD_AF_IPV4:
+ if (!(POKE_UINT8(1) && /* Type: NLPID */
+ POKE_UINT8(1) && /* Length: 1 */
+ POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */
+ POKE_UINT16(sizeof(
+ struct in_addr)) && /* Address length */
+ POKE_BYTES(&mgmt->m_addr,
+ sizeof(struct in_addr))))
+ goto toobig;
+ break;
+ }
+ }
+ if (!(POKE_END_CDP_TLV)) goto toobig;
+ }
+
+ /* Port ID */
+ const char *port_descr =
+ hardware->h_lport.p_descr ? hardware->h_lport.p_descr : "";
+ if (!(POKE_START_CDP_TLV(CDP_TLV_PORT) &&
+ POKE_BYTES(port_descr, strlen(port_descr)) && POKE_END_CDP_TLV))
+ goto toobig;
+
+ /* Capabilities */
+ if (version != 0) {
+ cap = 0;
+ if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) cap |= CDP_CAP_ROUTER;
+ if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) cap |= CDP_CAP_SWITCH;
+ cap |= CDP_CAP_HOST;
+ if (!(POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && POKE_UINT32(cap) &&
+ POKE_END_CDP_TLV))
+ goto toobig;
+# ifdef ENABLE_FDP
+ } else {
+ /* With FDP, it seems that a string is used in place of an int */
+ if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
+ capstr = "Router";
+ else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
+ capstr = "Switch";
+ else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
+ capstr = "Bridge";
+ else
+ capstr = "Host";
+ if (!(POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
+ POKE_BYTES(capstr, strlen(capstr)) && POKE_END_CDP_TLV))
+ goto toobig;
+# endif
+ }
+
+ /* Native VLAN */
+# ifdef ENABLE_DOT1
+ if (version >= 2 && hardware->h_lport.p_pvid != 0) {
+ if (!(POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) &&
+ POKE_UINT16(hardware->h_lport.p_pvid) && POKE_END_CDP_TLV))
+ goto toobig;
+ }
+# endif
+
+ /* Software version */
+ const char *chassis_descr = chassis->c_descr ? chassis->c_descr : "";
+ if (!(POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
+ POKE_BYTES(chassis_descr, strlen(chassis_descr)) && POKE_END_CDP_TLV))
+ goto toobig;
+
+ /* Platform */
+ if (global && global->g_config.c_platform)
+ platform = global->g_config.c_platform;
+
+ if (!(POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
+ POKE_BYTES(platform, strlen(platform)) && POKE_END_CDP_TLV))
+ goto toobig;
+
+# ifdef ENABLE_DOT3
+ if ((version >= 2) && (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) &&
+ (port->p_power.devicetype == LLDP_DOT3_POWER_PD) &&
+ (port->p_power.requested > 0) && (port->p_power.requested <= 655)) {
+ u_int16_t requested;
+ u_int16_t consumption;
+
+ if (port->p_power.requested != port->p_power.allocated) {
+ port->p_cdp_power.request_id++;
+ log_debug("cdp", "requested: %d, allocated:%d",
+ port->p_power.requested, port->p_power.allocated);
+ }
+ consumption = port->p_power.allocated ? port->p_power.allocated :
+ CDP_SWTICH_DEFAULT_POE_PD;
+ if (consumption > 130) {
+ consumption += CDP_SWITCH_POE_CLASS_4_OFFSET;
+ } else {
+ consumption += CDP_SWITCH_POE_CLASS_3_OFFSET;
+ }
+ if (port->p_power.requested > 130) { /* Class 4 */
+ requested =
+ port->p_power.requested + CDP_SWITCH_POE_CLASS_4_OFFSET;
+ } else { /* Class 3 */
+ requested =
+ port->p_power.requested + CDP_SWITCH_POE_CLASS_3_OFFSET;
+ }
+ if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
+ POKE_UINT16(consumption * 100) && POKE_END_CDP_TLV))
+ goto toobig;
+ /* Avoid request id 0 from overflow */
+ if (!port->p_cdp_power.request_id) {
+ port->p_cdp_power.request_id = 1;
+ }
+ if (!port->p_cdp_power.management_id) {
+ port->p_cdp_power.management_id = 1;
+ }
+ if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_REQUESTED) &&
+ POKE_UINT16(port->p_cdp_power.request_id) &&
+ POKE_UINT16(port->p_cdp_power.management_id) &&
+ POKE_UINT32(requested * 100) && POKE_END_CDP_TLV))
+ goto toobig;
+ }
+# elif defined ENABLE_LLDPMED
+ /* Power use */
+ if ((version >= 2) && port->p_med_cap_enabled &&
+ (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) &&
+ (port->p_med_power.val > 0) && (port->p_med_power.val <= 655)) {
+ if (!(POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
+ POKE_UINT16(port->p_med_power.val * 100) && POKE_END_CDP_TLV))
+ goto toobig;
+ }
+# endif
+
+ (void)POKE_SAVE(end);
+
+ /* Compute len and checksum */
+ POKE_RESTORE(pos_len_eh);
+ if (!(POKE_UINT16(end - pos_llc))) goto toobig;
+ checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0);
+ POKE_RESTORE(pos_checksum);
+ if (!(POKE_UINT16(checksum))) goto toobig;
+
+ if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) ==
+ -1) {
+ log_warn("cdp", "unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(packet);
+ return ENETDOWN;
+ }
+
+ hardware->h_tx_cnt++;
+
+ free(packet);
+ return 0;
+toobig:
+ free(packet);
+ return -1;
+}
+
+# define CHECK_TLV_SIZE(x, name) \
+ do { \
+ if (tlv_len < (x)) { \
+ log_warnx("cdp", name " CDP/FDP TLV too short received on %s", \
+ hardware->h_ifname); \
+ goto malformed; \
+ } \
+ } while (0)
+/* cdp_decode also decodes FDP */
+int
+cdp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct lldpd_mgmt *mgmt;
+ struct in_addr addr;
+# if 0
+ u_int16_t cksum;
+# endif
+ u_int8_t *software = NULL, *platform = NULL;
+ int software_len = 0, platform_len = 0, proto, version, nb, caps;
+ const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR;
+# ifdef ENABLE_FDP
+ const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR;
+ int fdp = 0;
+# endif
+ u_int8_t *pos, *tlv, *pos_address, *pos_next_address;
+ int length, len_eth, tlv_type, tlv_len, addresses_len, address_len;
+# ifdef ENABLE_DOT1
+ struct lldpd_vlan *vlan;
+# endif
+
+ log_debug("cdp", "decode CDP frame received on %s", hardware->h_ifname);
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ log_warn("cdp", "failed to allocate remote chassis");
+ return -1;
+ }
+ TAILQ_INIT(&chassis->c_mgmt);
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ log_warn("cdp", "failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+# ifdef ENABLE_DOT1
+ TAILQ_INIT(&port->p_vlans);
+# endif
+
+ length = s;
+ pos = (u_int8_t *)frame;
+
+ if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ +
+ 8 /* LLC */ + 4 /* CDP header */) {
+ log_warn("cdp", "too short CDP/FDP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) {
+# ifdef ENABLE_FDP
+ PEEK_RESTORE((u_int8_t *)frame);
+ if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0)
+ fdp = 1;
+ else {
+# endif
+ log_info("cdp",
+ "frame not targeted at CDP/FDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+# ifdef ENABLE_FDP
+ }
+# endif
+ }
+ PEEK_DISCARD(ETHER_ADDR_LEN); /* Don't care of source address */
+ len_eth = PEEK_UINT16;
+ if (len_eth > length) {
+ log_warnx("cdp", "incorrect 802.3 frame size reported on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ /* This is the correct length of the CDP + LLC packets */
+ length = len_eth;
+
+ PEEK_DISCARD(6); /* Skip beginning of LLC */
+ proto = PEEK_UINT16;
+ if (proto != LLC_PID_CDP) {
+ if ((proto != LLC_PID_DRIP) && (proto != LLC_PID_PAGP) &&
+ (proto != LLC_PID_PVSTP) && (proto != LLC_PID_UDLD) &&
+ (proto != LLC_PID_VTP) && (proto != LLC_PID_DTP) &&
+ (proto != LLC_PID_STP))
+ log_debug("cdp", "incorrect LLC protocol ID received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+# if 0
+ /* Check checksum */
+ cksum = frame_checksum(pos, len_eth - 8,
+# ifdef ENABLE_FDP
+ !fdp /* fdp = 0 -> cisco checksum */
+# else
+ 1 /* cisco checksum */
+# endif
+ );
+ if (cksum != 0) {
+ log_info("cdp", "incorrect CDP/FDP checksum for frame received on %s (%d)",
+ hardware->h_ifname, cksum);
+ goto malformed;
+ }
+# endif
+
+ /* Check version */
+ version = PEEK_UINT8;
+ if ((version != 1) && (version != 2)) {
+ log_warnx("cdp",
+ "incorrect CDP/FDP version (%d) for frame received on %s", version,
+ hardware->h_ifname);
+ goto malformed;
+ }
+ port->p_ttl = PEEK_UINT8; /* TTL */
+ PEEK_DISCARD_UINT16; /* Checksum, already checked */
+
+ while (length) {
+ if (length < 4) {
+ log_warnx("cdp",
+ "CDP/FDP TLV header is too large for "
+ "frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ tlv_type = PEEK_UINT16;
+ tlv_len = PEEK_UINT16 - 4;
+
+ (void)PEEK_SAVE(tlv);
+ if ((tlv_len < 0) || (length < tlv_len)) {
+ log_warnx("cdp",
+ "incorrect size in CDP/FDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ switch (tlv_type) {
+ case CDP_TLV_CHASSIS:
+ free(chassis->c_name);
+ if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) ==
+ NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for chassis name");
+ goto malformed;
+ }
+ PEEK_BYTES(chassis->c_name, tlv_len);
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
+ free(chassis->c_id);
+ if ((chassis->c_id = (char *)malloc(tlv_len)) == NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for chassis ID");
+ goto malformed;
+ }
+ memcpy(chassis->c_id, chassis->c_name, tlv_len);
+ chassis->c_id_len = tlv_len;
+ break;
+ case CDP_TLV_ADDRESSES:
+ CHECK_TLV_SIZE(4, "Address");
+ addresses_len = tlv_len - 4;
+ for (nb = PEEK_UINT32; nb > 0; nb--) {
+ (void)PEEK_SAVE(pos_address);
+ /* We first try to get the real length of the packet */
+ if (addresses_len < 2) {
+ log_warn("cdp",
+ "too short address subframe "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD_UINT8;
+ addresses_len--;
+ address_len = PEEK_UINT8;
+ addresses_len--;
+ if (addresses_len < address_len + 2) {
+ log_warn("cdp",
+ "too short address subframe "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD(address_len);
+ addresses_len -= address_len;
+ address_len = PEEK_UINT16;
+ addresses_len -= 2;
+ if (addresses_len < address_len) {
+ log_warn("cdp",
+ "too short address subframe "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD(address_len);
+ addresses_len -= address_len;
+ (void)PEEK_SAVE(pos_next_address);
+ /* Next, we go back and try to extract
+ IPv4 address */
+ PEEK_RESTORE(pos_address);
+ if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) &&
+ (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) &&
+ (PEEK_UINT16 == sizeof(struct in_addr))) {
+ PEEK_BYTES(&addr, sizeof(struct in_addr));
+ mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &addr,
+ sizeof(struct in_addr), 0);
+ if (mgmt == NULL) {
+ if (errno == ENOMEM)
+ log_warn("cdp",
+ "unable to allocate memory for management address");
+ else
+ log_warn("cdp",
+ "too large management address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt,
+ m_entries);
+ }
+ /* Go to the end of the address */
+ PEEK_RESTORE(pos_next_address);
+ }
+ break;
+ case CDP_TLV_PORT:
+ if (tlv_len == 0) {
+ log_warn("cdp", "too short port description received");
+ goto malformed;
+ }
+ free(port->p_descr);
+ if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for port description");
+ goto malformed;
+ }
+ PEEK_BYTES(port->p_descr, tlv_len);
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ free(port->p_id);
+ if ((port->p_id = (char *)calloc(1, tlv_len)) == NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for port ID");
+ goto malformed;
+ }
+ memcpy(port->p_id, port->p_descr, tlv_len);
+ port->p_id_len = tlv_len;
+ break;
+ case CDP_TLV_CAPABILITIES:
+# ifdef ENABLE_FDP
+ if (fdp) {
+ /* Capabilities are string with FDP */
+ if (!strncmp("Router", (char *)pos, tlv_len))
+ chassis->c_cap_enabled = LLDP_CAP_ROUTER;
+ else if (!strncmp("Switch", (char *)pos, tlv_len))
+ chassis->c_cap_enabled = LLDP_CAP_BRIDGE;
+ else if (!strncmp("Bridge", (char *)pos, tlv_len))
+ chassis->c_cap_enabled = LLDP_CAP_REPEATER;
+ else
+ chassis->c_cap_enabled = LLDP_CAP_STATION;
+ chassis->c_cap_available = chassis->c_cap_enabled;
+ break;
+ }
+# endif
+ CHECK_TLV_SIZE(4, "Capabilities");
+ caps = PEEK_UINT32;
+ if (caps & CDP_CAP_ROUTER)
+ chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
+ if (caps & 0x0e) chassis->c_cap_enabled |= LLDP_CAP_BRIDGE;
+ if (chassis->c_cap_enabled == 0)
+ chassis->c_cap_enabled = LLDP_CAP_STATION;
+ chassis->c_cap_available = chassis->c_cap_enabled;
+ break;
+ case CDP_TLV_SOFTWARE:
+ software_len = tlv_len;
+ (void)PEEK_SAVE(software);
+ break;
+ case CDP_TLV_PLATFORM:
+ platform_len = tlv_len;
+ (void)PEEK_SAVE(platform);
+ break;
+# ifdef ENABLE_DOT1
+ case CDP_TLV_NATIVEVLAN:
+ CHECK_TLV_SIZE(2, "Native VLAN");
+ if ((vlan = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ log_warn("cdp",
+ "unable to alloc vlan "
+ "structure for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ vlan->v_vid = port->p_pvid = PEEK_UINT16;
+ if (asprintf(&vlan->v_name, "VLAN #%d", vlan->v_vid) == -1) {
+ log_warn("cdp",
+ "unable to alloc VLAN name for "
+ "TLV received on %s",
+ hardware->h_ifname);
+ free(vlan);
+ goto malformed;
+ }
+ TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
+ break;
+# endif
+# ifdef ENABLE_DOT3
+ case CDP_TLV_POWER_AVAILABLE:
+ CHECK_TLV_SIZE(12, "Power Available");
+ /* check if it is a respone to a request id */
+ if (PEEK_UINT16 > 0) {
+ port->p_cdp_power.management_id = PEEK_UINT16;
+ port->p_power.allocated = PEEK_UINT32;
+ port->p_power.allocated /= 100;
+ port->p_power.supported = 1;
+ port->p_power.enabled = 1;
+ port->p_power.devicetype = LLDP_DOT3_POWER_PSE;
+ port->p_power.powertype = LLDP_DOT3_POWER_8023AT_TYPE2;
+ log_debug("cdp", "Allocated power %d00",
+ port->p_power.allocated);
+ if (port->p_power.allocated > CDP_CLASS_3_MAX_PSE_POE) {
+ port->p_power.allocated -=
+ CDP_SWITCH_POE_CLASS_4_OFFSET;
+ } else if (port->p_power.allocated >
+ CDP_SWITCH_POE_CLASS_3_OFFSET) {
+ port->p_power.allocated -=
+ CDP_SWITCH_POE_CLASS_3_OFFSET;
+ } else {
+ port->p_power.allocated = 0;
+ }
+ port->p_power.requested =
+ hardware->h_lport.p_power.requested;
+ }
+ break;
+# endif
+ default:
+ log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s",
+ ntohs(tlv_type), hardware->h_ifname);
+ hardware->h_rx_unrecognized_cnt++;
+ }
+ PEEK_DISCARD(tlv + tlv_len - pos);
+ }
+ if (!software && platform) {
+ if ((chassis->c_descr = (char *)calloc(1, platform_len + 1)) == NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, platform, platform_len);
+ } else if (software && !platform) {
+ if ((chassis->c_descr = (char *)calloc(1, software_len + 1)) == NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, software, software_len);
+ } else if (software && platform) {
+# define CONCAT_PLATFORM " running on\n"
+ if ((chassis->c_descr = (char *)calloc(1,
+ software_len + platform_len + strlen(CONCAT_PLATFORM) + 1)) ==
+ NULL) {
+ log_warn("cdp",
+ "unable to allocate memory for chassis description");
+ goto malformed;
+ }
+ memcpy(chassis->c_descr, platform, platform_len);
+ memcpy(chassis->c_descr + platform_len, CONCAT_PLATFORM,
+ strlen(CONCAT_PLATFORM));
+ memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM),
+ software, software_len);
+ }
+ if ((chassis->c_id == NULL) || (port->p_id == NULL) ||
+ (chassis->c_name == NULL) || (chassis->c_descr == NULL) ||
+ (port->p_descr == NULL) || (port->p_ttl == 0) ||
+ (chassis->c_cap_enabled == 0)) {
+ log_warnx("cdp",
+ "some mandatory CDP/FDP tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+ lldpd_chassis_cleanup(chassis, 1);
+ lldpd_port_cleanup(port, 1);
+ free(port);
+ return -1;
+}
+
+# ifdef ENABLE_CDP
+int
+cdpv1_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ return cdp_send(global, hardware, 1);
+}
+
+int
+cdpv2_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ return cdp_send(global, hardware, 2);
+}
+# endif
+
+# ifdef ENABLE_FDP
+int
+fdp_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ return cdp_send(global, hardware, 0);
+}
+# endif
+
+# ifdef ENABLE_CDP
+static int
+cdp_guess(char *pos, int length, int version)
+{
+ const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
+ if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ +
+ 8 /* LLC */ + 4 /* CDP header */)
+ return 0;
+ if (PEEK_CMP(mcastaddr, ETHER_ADDR_LEN) != 0) return 0;
+ PEEK_DISCARD(ETHER_ADDR_LEN);
+ PEEK_DISCARD_UINT16; /* Ethernet */
+ PEEK_DISCARD(8); /* LLC */
+ return (PEEK_UINT8 == version);
+}
+
+int
+cdpv1_guess(char *frame, int len)
+{
+ return cdp_guess(frame, len, 1);
+}
+
+int
+cdpv2_guess(char *frame, int len)
+{
+ return cdp_guess(frame, len, 2);
+}
+# endif
+
+#endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */
diff --git a/src/daemon/protocols/cdp.h b/src/daemon/protocols/cdp.h
new file mode 100644
index 0000000..f83ffef
--- /dev/null
+++ b/src/daemon/protocols/cdp.h
@@ -0,0 +1,70 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _CDP_H
+#define _CDP_H
+
+#define CDP_MULTICAST_ADDR \
+ { \
+ 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc \
+ }
+#define FDP_MULTICAST_ADDR \
+ { \
+ 0x01, 0xe0, 0x52, 0xcc, 0xcc, 0xcc, \
+ }
+#define LLC_ORG_CISCO \
+ { \
+ 0x00, 0x00, 0x0c \
+ }
+#define LLC_ORG_FOUNDRY \
+ { \
+ 0x00, 0xe0, 0x52 \
+ }
+#define LLC_PID_CDP 0x2000
+/* Other protocols */
+#define LLC_PID_DRIP 0x102
+#define LLC_PID_PAGP 0x104
+#define LLC_PID_PVSTP 0x10b
+#define LLC_PID_UDLD 0x111
+#define LLC_PID_VTP 0x2003
+#define LLC_PID_DTP 0x2004
+#define LLC_PID_STP 0x200a
+
+enum {
+ CDP_TLV_CHASSIS = 1,
+ CDP_TLV_ADDRESSES = 2,
+ CDP_TLV_PORT = 3,
+ CDP_TLV_CAPABILITIES = 4,
+ CDP_TLV_SOFTWARE = 5,
+ CDP_TLV_PLATFORM = 6,
+ CDP_TLV_NATIVEVLAN = 10,
+ CDP_TLV_POWER_CONSUMPTION = 0x10,
+ CDP_TLV_POWER_REQUESTED = 0x19,
+ CDP_TLV_POWER_AVAILABLE = 0x1A
+};
+
+#define CDP_ADDRESS_PROTO_IP 0xcc
+
+#define CDP_CAP_ROUTER 0x01
+#define CDP_CAP_TRANSPARENT_BRIDGE 0x02
+#define CDP_CAP_SOURCE_BRIDGE 0x04
+#define CDP_CAP_SWITCH 0x08
+#define CDP_CAP_HOST 0x10
+#define CDP_CAP_IGMP 0x20
+#define CDP_CAP_REPEATER 0x40
+
+#endif /* _CDP_H */
diff --git a/src/daemon/protocols/edp.c b/src/daemon/protocols/edp.c
new file mode 100644
index 0000000..b55130b
--- /dev/null
+++ b/src/daemon/protocols/edp.c
@@ -0,0 +1,514 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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 "../frame.h"
+
+#ifdef ENABLE_EDP
+
+# include <stdio.h>
+# include <unistd.h>
+# include <errno.h>
+# include <arpa/inet.h>
+# include <fnmatch.h>
+
+static int seq = 0;
+
+int
+edp_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
+ const u_int8_t llcorg[] = LLC_ORG_EXTREME;
+ struct lldpd_chassis *chassis;
+ int length, i, v;
+ u_int8_t *packet, *pos, *pos_llc, *pos_len_eh, *pos_len_edp, *pos_edp, *tlv,
+ *end;
+ u_int16_t checksum;
+# ifdef ENABLE_DOT1
+ struct lldpd_vlan *vlan;
+ unsigned int state = 0;
+# endif
+ u_int8_t edp_fakeversion[] = { 7, 6, 4, 99 };
+ /* Subsequent XXX can be replaced by other values. We place
+ them here to ensure the position of "" to be a bit
+ invariant with version changes. */
+ const char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX",
+ "XXX", "", NULL };
+
+ log_debug("edp", "send EDP frame on port %s", hardware->h_ifname);
+
+ chassis = hardware->h_lport.p_chassis;
+# ifdef ENABLE_DOT1
+ while (state != 2) {
+# endif
+ length = hardware->h_mtu;
+ if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM;
+ pos = packet;
+ v = 0;
+
+ /* Ethernet header */
+ if (!(POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+ POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
+ POKE_SAVE(pos_len_eh) && /* We compute the len later */
+ POKE_UINT16(0)))
+ goto toobig;
+
+ /* LLC */
+ if (!(POKE_SAVE(pos_llc) && /* We need to save our
+ current position to
+ compute ethernet len */
+ /* SSAP and DSAP */
+ POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
+ /* Control field */
+ POKE_UINT8(0x03) &&
+ /* ORG */
+ POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_EDP)))
+ goto toobig;
+
+ /* EDP header */
+ if ((chassis->c_id_len != ETHER_ADDR_LEN) ||
+ (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
+ log_warnx("edp",
+ "local chassis does not use MAC address as chassis ID!?");
+ free(packet);
+ return EINVAL;
+ }
+ if (!(POKE_SAVE(pos_edp) && /* Save the start of EDP frame */
+ POKE_UINT8(1) && POKE_UINT8(0) &&
+ POKE_SAVE(pos_len_edp) && /* We compute the len
+ and the checksum
+ later */
+ POKE_UINT32(0) && /* Len + Checksum */
+ POKE_UINT16(seq) && POKE_UINT16(0) &&
+ POKE_BYTES(chassis->c_id, ETHER_ADDR_LEN)))
+ goto toobig;
+ seq++;
+
+# ifdef ENABLE_DOT1
+ switch (state) {
+ case 0:
+# endif
+ /* Display TLV */
+ if (!(POKE_START_EDP_TLV(EDP_TLV_DISPLAY) &&
+ POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+ POKE_UINT8(0) && /* Add a NULL character
+ for better
+ compatibility */
+ POKE_END_EDP_TLV))
+ goto toobig;
+
+ /* Info TLV */
+ if (!(POKE_START_EDP_TLV(EDP_TLV_INFO))) goto toobig;
+ /* We try to emulate the slot thing */
+ for (i = 0; deviceslot[i] != NULL; i++) {
+ if (strncmp(hardware->h_ifname, deviceslot[i],
+ strlen(deviceslot[i])) == 0) {
+ if (!(POKE_UINT16(i) &&
+ POKE_UINT16(atoi(hardware->h_ifname +
+ strlen(deviceslot[i])))))
+ goto toobig;
+ break;
+ }
+ }
+ /* If we don't find a "slot", we say that the
+ interface is in slot 8 */
+ if (deviceslot[i] == NULL) {
+ if (!(POKE_UINT16(8) &&
+ POKE_UINT16(hardware->h_ifindex)))
+ goto toobig;
+ }
+ if (!(POKE_UINT16(0) && /* vchassis */
+ POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */
+ /* Version */
+ POKE_BYTES(edp_fakeversion, sizeof(edp_fakeversion)) &&
+ /* Connections, we say that we won't
+ have more interfaces than this
+ mask. */
+ POKE_UINT32(0xffffffff) && POKE_UINT32(0) &&
+ POKE_UINT32(0) && POKE_UINT32(0) && POKE_END_EDP_TLV))
+ goto toobig;
+
+# ifdef ENABLE_DOT1
+ break;
+ case 1:
+ TAILQ_FOREACH (vlan, &hardware->h_lport.p_vlans, v_entries) {
+ v++;
+ if (!(POKE_START_EDP_TLV(EDP_TLV_VLAN) &&
+ POKE_UINT8(0) && /* Flags: no IP address */
+ POKE_UINT8(0) && /* Reserved */
+ POKE_UINT16(vlan->v_vid) &&
+ POKE_UINT32(0) && /* Reserved */
+ POKE_UINT32(0) && /* IP address */
+ /* VLAN name */
+ POKE_BYTES(vlan->v_name,
+ strlen(vlan->v_name)) &&
+ POKE_UINT8(0) && POKE_END_EDP_TLV))
+ goto toobig;
+ }
+ break;
+ }
+
+ if ((state == 1) && (v == 0)) {
+ /* No VLAN, no need to send another TLV */
+ free(packet);
+ break;
+ }
+# endif
+
+ /* Null TLV */
+ if (!(POKE_START_EDP_TLV(EDP_TLV_NULL) && POKE_END_EDP_TLV &&
+ POKE_SAVE(end)))
+ goto toobig;
+
+ /* Compute len and checksum */
+ i = end - pos_llc; /* Ethernet length */
+ v = end - pos_edp; /* EDP length */
+ POKE_RESTORE(pos_len_eh);
+ if (!(POKE_UINT16(i))) goto toobig;
+ POKE_RESTORE(pos_len_edp);
+ if (!(POKE_UINT16(v))) goto toobig;
+ checksum = frame_checksum(pos_edp, v, 0);
+ if (!(POKE_UINT16(checksum))) goto toobig;
+
+ if (interfaces_send_helper(global, hardware, (char *)packet,
+ end - packet) == -1) {
+ log_warn("edp", "unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(packet);
+ return ENETDOWN;
+ }
+ free(packet);
+
+# ifdef ENABLE_DOT1
+ state++;
+ }
+# endif
+
+ hardware->h_tx_cnt++;
+ return 0;
+toobig:
+ free(packet);
+ return E2BIG;
+}
+
+# define CHECK_TLV_SIZE(x, name) \
+ do { \
+ if (tlv_len < (x)) { \
+ log_warnx("edp", name " EDP TLV too short received on %s", \
+ hardware->h_ifname); \
+ goto malformed; \
+ } \
+ } while (0)
+
+int
+edp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+# ifdef ENABLE_DOT1
+ struct lldpd_mgmt *mgmt, *mgmt_next, *m;
+ struct lldpd_vlan *lvlan = NULL, *lvlan_next;
+# endif
+ const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
+ int length, gotend = 0, gotvlans = 0, edp_len, tlv_len, tlv_type;
+ int edp_port, edp_slot;
+ u_int8_t *pos, *pos_edp, *tlv;
+ u_int8_t version[4];
+# ifdef ENABLE_DOT1
+ struct in_addr address;
+ struct lldpd_port *oport;
+# endif
+
+ log_debug("edp", "decode EDP frame on port %s", hardware->h_ifname);
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ log_warn("edp", "failed to allocate remote chassis");
+ return -1;
+ }
+ TAILQ_INIT(&chassis->c_mgmt);
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ log_warn("edp", "failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+# ifdef ENABLE_DOT1
+ TAILQ_INIT(&port->p_vlans);
+# endif
+
+ length = s;
+ pos = (u_int8_t *)frame;
+
+ if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) + 8 /* LLC */ + 10 +
+ ETHER_ADDR_LEN /* EDP header */) {
+ log_warnx("edp", "too short EDP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ if (PEEK_CMP(edpaddr, sizeof(edpaddr)) != 0) {
+ log_info("edp",
+ "frame not targeted at EDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD(ETHER_ADDR_LEN);
+ PEEK_DISCARD_UINT16;
+ PEEK_DISCARD(6); /* LLC: DSAP + SSAP + control + org */
+ if (PEEK_UINT16 != LLC_PID_EDP) {
+ log_debug("edp", "incorrect LLC protocol ID received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ (void)PEEK_SAVE(pos_edp); /* Save the start of EDP packet */
+ if (PEEK_UINT8 != 1) {
+ log_warnx("edp", "incorrect EDP version for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD_UINT8; /* Reserved */
+ edp_len = PEEK_UINT16;
+ PEEK_DISCARD_UINT16; /* Checksum */
+ PEEK_DISCARD_UINT16; /* Sequence */
+ if (PEEK_UINT16 != 0) { /* ID Type = 0 = MAC */
+ log_warnx("edp", "incorrect device id type for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (edp_len > length + 10) {
+ log_warnx("edp", "incorrect size for EDP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ port->p_ttl = cfg ? cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold : 0;
+ port->p_ttl = (port->p_ttl + 999) / 1000;
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+ chassis->c_id_len = ETHER_ADDR_LEN;
+ if ((chassis->c_id = (char *)malloc(ETHER_ADDR_LEN)) == NULL) {
+ log_warn("edp", "unable to allocate memory for chassis ID");
+ goto malformed;
+ }
+ PEEK_BYTES(chassis->c_id, ETHER_ADDR_LEN);
+
+# ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ /* Let's check checksum */
+ if (frame_checksum(pos_edp, edp_len, 0) != 0) {
+ log_warnx("edp", "incorrect EDP checksum for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+# endif
+
+ while (length && !gotend) {
+ if (length < 4) {
+ log_warnx("edp",
+ "EDP TLV header is too large for "
+ "frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (PEEK_UINT8 != EDP_TLV_MARKER) {
+ log_warnx("edp",
+ "incorrect marker starting EDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ tlv_type = PEEK_UINT8;
+ tlv_len = PEEK_UINT16 - 4;
+ (void)PEEK_SAVE(tlv);
+ if ((tlv_len < 0) || (tlv_len > length)) {
+ log_debug("edp",
+ "incorrect size in EDP TLV header for frame "
+ "received on %s",
+ hardware->h_ifname);
+ /* Some poor old Extreme Summit are quite bogus */
+ gotend = 1;
+ break;
+ }
+ switch (tlv_type) {
+ case EDP_TLV_INFO:
+ CHECK_TLV_SIZE(32, "Info");
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+ edp_slot = PEEK_UINT16;
+ edp_port = PEEK_UINT16;
+ free(port->p_id);
+ port->p_id_len =
+ asprintf(&port->p_id, "%d/%d", edp_slot + 1, edp_port + 1);
+ if (port->p_id_len == -1) {
+ log_warn("edp",
+ "unable to allocate memory for "
+ "port ID");
+ goto malformed;
+ }
+ free(port->p_descr);
+ if (asprintf(&port->p_descr, "Slot %d / Port %d", edp_slot + 1,
+ edp_port + 1) == -1) {
+ log_warn("edp",
+ "unable to allocate memory for "
+ "port description");
+ goto malformed;
+ }
+ PEEK_DISCARD_UINT16; /* vchassis */
+ PEEK_DISCARD(6); /* Reserved */
+ PEEK_BYTES(version, 4);
+ free(chassis->c_descr);
+ if (asprintf(&chassis->c_descr,
+ "EDP enabled device, version %d.%d.%d.%d", version[0],
+ version[1], version[2], version[3]) == -1) {
+ log_warn("edp",
+ "unable to allocate memory for "
+ "chassis description");
+ goto malformed;
+ }
+ break;
+ case EDP_TLV_DISPLAY:
+ free(chassis->c_name);
+ if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) ==
+ NULL) {
+ log_warn("edp",
+ "unable to allocate memory for chassis "
+ "name");
+ goto malformed;
+ }
+ /* TLV display contains a lot of garbage */
+ PEEK_BYTES(chassis->c_name, tlv_len);
+ break;
+ case EDP_TLV_NULL:
+ if (tlv_len != 0) {
+ log_warnx("edp",
+ "null tlv with incorrect size in frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (length)
+ log_debug("edp", "extra data after edp frame on %s",
+ hardware->h_ifname);
+ gotend = 1;
+ break;
+ case EDP_TLV_VLAN:
+# ifdef ENABLE_DOT1
+ CHECK_TLV_SIZE(12, "VLAN");
+ if ((lvlan = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ log_warn("edp", "unable to allocate vlan");
+ goto malformed;
+ }
+ PEEK_DISCARD_UINT16; /* Flags + reserved */
+ lvlan->v_vid = PEEK_UINT16; /* VID */
+ PEEK_DISCARD(4); /* Reserved */
+ PEEK_BYTES(&address, sizeof(address));
+
+ if (address.s_addr != INADDR_ANY) {
+ mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address,
+ sizeof(struct in_addr), 0);
+ if (mgmt == NULL) {
+ log_warn("edp", "Out of memory");
+ goto malformed;
+ }
+ TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
+ }
+
+ if ((lvlan->v_name = (char *)calloc(1, tlv_len + 1 - 12)) ==
+ NULL) {
+ log_warn("edp", "unable to allocate vlan name");
+ goto malformed;
+ }
+ PEEK_BYTES(lvlan->v_name, tlv_len - 12);
+
+ TAILQ_INSERT_TAIL(&port->p_vlans, lvlan, v_entries);
+ lvlan = NULL;
+# endif
+ gotvlans = 1;
+ break;
+ default:
+ log_debug("edp", "unknown EDP TLV type (%d) received on %s",
+ tlv_type, hardware->h_ifname);
+ hardware->h_rx_unrecognized_cnt++;
+ }
+ PEEK_DISCARD(tlv + tlv_len - pos);
+ }
+ if ((chassis->c_id == NULL) || (port->p_id == NULL) ||
+ (chassis->c_name == NULL) || (chassis->c_descr == NULL) ||
+ (port->p_descr == NULL) || (gotend == 0)) {
+# ifdef ENABLE_DOT1
+ if (gotvlans && gotend) {
+ /* VLAN can be sent in a separate frames. We need to add
+ * those vlans to an existing port */
+ TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) {
+ if (!((oport->p_protocol == LLDPD_MODE_EDP) &&
+ (oport->p_chassis->c_id_subtype ==
+ chassis->c_id_subtype) &&
+ (oport->p_chassis->c_id_len ==
+ chassis->c_id_len) &&
+ (memcmp(oport->p_chassis->c_id, chassis->c_id,
+ chassis->c_id_len) == 0)))
+ continue;
+ /* We attach the VLANs to the found port */
+ lldpd_vlan_cleanup(oport);
+ for (lvlan = TAILQ_FIRST(&port->p_vlans); lvlan != NULL;
+ lvlan = lvlan_next) {
+ lvlan_next = TAILQ_NEXT(lvlan, v_entries);
+ TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries);
+ TAILQ_INSERT_TAIL(&oport->p_vlans, lvlan,
+ v_entries);
+ }
+ /* And the IP addresses */
+ for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL;
+ mgmt = mgmt_next) {
+ mgmt_next = TAILQ_NEXT(mgmt, m_entries);
+ TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
+ /* Don't add an address that already exists! */
+ TAILQ_FOREACH (m, &chassis->c_mgmt, m_entries)
+ if (m->m_family == mgmt->m_family &&
+ !memcmp(&m->m_addr, &mgmt->m_addr,
+ sizeof(m->m_addr)))
+ break;
+ if (m == NULL)
+ TAILQ_INSERT_TAIL(
+ &oport->p_chassis->c_mgmt, mgmt,
+ m_entries);
+ }
+ }
+ /* We discard the remaining frame */
+ goto malformed;
+ }
+# else
+ if (gotvlans) goto malformed;
+# endif
+ log_warnx("edp",
+ "some mandatory tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+# ifdef ENABLE_DOT1
+ free(lvlan);
+# endif
+ lldpd_chassis_cleanup(chassis, 1);
+ lldpd_port_cleanup(port, 1);
+ free(port);
+ return -1;
+}
+
+#endif /* ENABLE_EDP */
diff --git a/src/daemon/protocols/edp.h b/src/daemon/protocols/edp.h
new file mode 100644
index 0000000..f4ee183
--- /dev/null
+++ b/src/daemon/protocols/edp.h
@@ -0,0 +1,41 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _EDP_H
+#define _EDP_H
+
+#define EDP_MULTICAST_ADDR \
+ { \
+ 0x00, 0xe0, 0x2b, 0x00, 0x00, 0x00 \
+ }
+#define LLC_ORG_EXTREME \
+ { \
+ 0x00, 0xe0, 0x2b \
+ }
+#define LLC_PID_EDP 0x00bb
+
+#define EDP_TLV_MARKER 0x99
+
+enum {
+ EDP_TLV_NULL = 0,
+ EDP_TLV_DISPLAY = 1,
+ EDP_TLV_INFO = 2,
+ EDP_TLV_VLAN = 5,
+ EDP_TLV_ESRP = 8,
+};
+
+#endif /* _EDP_H */
diff --git a/src/daemon/protocols/lldp.c b/src/daemon/protocols/lldp.c
new file mode 100644
index 0000000..6e73237
--- /dev/null
+++ b/src/daemon/protocols/lldp.c
@@ -0,0 +1,1315 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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 "../frame.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+static int
+lldpd_af_to_lldp_proto(int af)
+{
+ switch (af) {
+ case LLDPD_AF_IPV4:
+ return LLDP_MGMT_ADDR_IP4;
+ case LLDPD_AF_IPV6:
+ return LLDP_MGMT_ADDR_IP6;
+ default:
+ return LLDP_MGMT_ADDR_NONE;
+ }
+}
+
+static int
+lldpd_af_from_lldp_proto(int proto)
+{
+ switch (proto) {
+ case LLDP_MGMT_ADDR_IP4:
+ return LLDPD_AF_IPV4;
+ case LLDP_MGMT_ADDR_IP6:
+ return LLDPD_AF_IPV6;
+ default:
+ return LLDPD_AF_UNSPEC;
+ }
+}
+
+static int
+_lldp_send(struct lldpd *global, struct lldpd_hardware *hardware, u_int8_t c_id_subtype,
+ char *c_id, int c_id_len, u_int8_t p_id_subtype, char *p_id, int p_id_len,
+ int shutdown, int without_vlans)
+{
+ struct lldpd_port *port;
+ struct lldpd_chassis *chassis;
+ struct lldpd_frame *frame;
+ int length;
+ u_int8_t *packet, *pos, *tlv;
+ struct lldpd_mgmt *mgmt;
+ int proto;
+ int vlans = 0;
+
+ u_int8_t mcastaddr_regular[] = LLDP_ADDR_NEAREST_BRIDGE;
+ u_int8_t mcastaddr_nontpmr[] = LLDP_ADDR_NEAREST_NONTPMR_BRIDGE;
+ u_int8_t mcastaddr_customer[] = LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE;
+ u_int8_t *mcastaddr;
+#ifdef ENABLE_DOT1
+ const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1;
+ struct lldpd_vlan *vlan;
+ struct lldpd_ppvid *ppvid;
+ struct lldpd_pi *pi;
+#endif
+#ifdef ENABLE_DOT3
+ const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3;
+#endif
+#ifdef ENABLE_LLDPMED
+ int i;
+ const u_int8_t med[] = LLDP_TLV_ORG_MED;
+#endif
+#ifdef ENABLE_CUSTOM
+ struct lldpd_custom *custom;
+#endif
+ port = &hardware->h_lport;
+ chassis = port->p_chassis;
+ length = hardware->h_mtu;
+ if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM;
+ pos = packet;
+
+ /* Ethernet header */
+ switch (global->g_config.c_lldp_agent_type) {
+ case LLDP_AGENT_TYPE_NEAREST_NONTPMR_BRIDGE:
+ mcastaddr = mcastaddr_nontpmr;
+ break;
+ case LLDP_AGENT_TYPE_NEAREST_CUSTOMER_BRIDGE:
+ mcastaddr = mcastaddr_customer;
+ break;
+ case LLDP_AGENT_TYPE_NEAREST_BRIDGE:
+ default:
+ mcastaddr = mcastaddr_regular;
+ break;
+ }
+ if (!(
+ /* LLDP multicast address */
+ POKE_BYTES(mcastaddr, ETHER_ADDR_LEN) &&
+ /* Source MAC address */
+ POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN)))
+ goto toobig;
+
+ /* Insert VLAN tag if needed */
+ if (port->p_vlan_tx_enabled) {
+ if (!(
+ /* VLAN ethertype */
+ POKE_UINT16(ETHERTYPE_VLAN) &&
+ /* VLAN Tag Control Information (TCI) */
+ /* Priority(3bits) | DEI(1bit) | VID(12bit) */
+ POKE_UINT16(port->p_vlan_tx_tag)))
+ goto toobig;
+ }
+
+ if (!(
+ /* LLDP frame */
+ POKE_UINT16(ETH_P_LLDP)))
+ goto toobig;
+
+ /* Chassis ID */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && POKE_UINT8(c_id_subtype) &&
+ POKE_BYTES(c_id, c_id_len) && POKE_END_LLDP_TLV))
+ goto toobig;
+
+ /* Port ID */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && POKE_UINT8(p_id_subtype) &&
+ POKE_BYTES(p_id, p_id_len) && POKE_END_LLDP_TLV))
+ goto toobig;
+
+ /* Time to live */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_TTL) &&
+ POKE_UINT16(shutdown ? 0 : (global ? global->g_config.c_ttl : 180)) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+
+ if (shutdown) goto end;
+
+ /* System name */
+ if (chassis->c_name && *chassis->c_name != '\0') {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) &&
+ POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+ /* System description (skip it if empty) */
+ if (chassis->c_descr && *chassis->c_descr != '\0') {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) &&
+ POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+ /* System capabilities */
+ if (global->g_config.c_cap_advertise && chassis->c_cap_available) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) &&
+ POKE_UINT16(chassis->c_cap_available) &&
+ POKE_UINT16(chassis->c_cap_enabled) && POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+ /* Management addresses */
+ TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) {
+ proto = lldpd_af_to_lldp_proto(mgmt->m_family);
+ if (proto == LLDP_MGMT_ADDR_NONE) continue;
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) &&
+ /* Size of the address, including its type */
+ POKE_UINT8(mgmt->m_addrsize + 1) && POKE_UINT8(proto) &&
+ POKE_BYTES(&mgmt->m_addr, mgmt->m_addrsize)))
+ goto toobig;
+
+ /* Interface port type, OID */
+ if (mgmt->m_iface == 0) {
+ if (!(
+ /* We don't know the management interface */
+ POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && POKE_UINT32(0)))
+ goto toobig;
+ } else {
+ if (!(
+ /* We have the index of the management interface */
+ POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) &&
+ POKE_UINT32(mgmt->m_iface)))
+ goto toobig;
+ }
+ if (!(
+ /* We don't provide an OID for management */
+ POKE_UINT8(0) && POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+ /* Port description */
+ if (port->p_descr && *port->p_descr != '\0') {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) &&
+ POKE_BYTES(port->p_descr, strlen(port->p_descr)) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+#ifdef ENABLE_DOT1
+ /* Port VLAN ID */
+ if (port->p_pvid != 0) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(dot1, sizeof(dot1)) &&
+ POKE_UINT8(LLDP_TLV_DOT1_PVID) && POKE_UINT16(port->p_pvid) &&
+ POKE_END_LLDP_TLV)) {
+ goto toobig;
+ }
+ }
+ /* Port and Protocol VLAN IDs */
+ TAILQ_FOREACH (ppvid, &port->p_ppvids, p_entries) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(dot1, sizeof(dot1)) &&
+ POKE_UINT8(LLDP_TLV_DOT1_PPVID) &&
+ POKE_UINT8(ppvid->p_cap_status) &&
+ POKE_UINT16(ppvid->p_ppvid) && POKE_END_LLDP_TLV)) {
+ goto toobig;
+ }
+ }
+ /* VLANs */
+ if (!without_vlans) {
+ TAILQ_FOREACH (vlan, &port->p_vlans, v_entries) {
+ vlans++;
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(dot1, sizeof(dot1)) &&
+ POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) &&
+ POKE_UINT16(vlan->v_vid) &&
+ POKE_UINT8(strlen(vlan->v_name)) &&
+ POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+ }
+ /* Protocol Identities */
+ TAILQ_FOREACH (pi, &port->p_pids, p_entries) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(dot1, sizeof(dot1)) &&
+ POKE_UINT8(LLDP_TLV_DOT1_PI) && POKE_UINT8(pi->p_pi_len) &&
+ POKE_BYTES(pi->p_pi, pi->p_pi_len) && POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+#endif
+
+#ifdef ENABLE_DOT3
+ /* Aggregation status */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(dot3, sizeof(dot3)) &&
+ POKE_UINT8(LLDP_TLV_DOT3_LA) &&
+ /* Bit 0 = capability ; Bit 1 = status */
+ POKE_UINT8((port->p_aggregid) ? 3 : 1) &&
+ POKE_UINT32(port->p_aggregid) && POKE_END_LLDP_TLV))
+ goto toobig;
+
+ /* MAC/PHY */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(dot3, sizeof(dot3)) &&
+ POKE_UINT8(LLDP_TLV_DOT3_MAC) &&
+ POKE_UINT8(port->p_macphy.autoneg_support |
+ (port->p_macphy.autoneg_enabled << 1)) &&
+ POKE_UINT16(port->p_macphy.autoneg_advertised) &&
+ POKE_UINT16(port->p_macphy.mau_type) && POKE_END_LLDP_TLV))
+ goto toobig;
+
+ /* MFS */
+ if (port->p_mfs) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(dot3, sizeof(dot3)) &&
+ POKE_UINT8(LLDP_TLV_DOT3_MFS) && POKE_UINT16(port->p_mfs) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+ /* Power */
+ if (port->p_power.devicetype) {
+ if (!((POKE_START_LLDP_TLV(LLDP_TLV_ORG)) &&
+ POKE_BYTES(dot3, sizeof(dot3)) &&
+ POKE_UINT8(LLDP_TLV_DOT3_POWER) &&
+ POKE_UINT8(((((2 - port->p_power.devicetype) % (1 << 1)) << 0) |
+ ((port->p_power.supported % (1 << 1)) << 1) |
+ ((port->p_power.enabled % (1 << 1)) << 2) |
+ ((port->p_power.paircontrol % (1 << 1)) << 3))) &&
+ POKE_UINT8(port->p_power.pairs) &&
+ POKE_UINT8(port->p_power.class)))
+ goto toobig;
+ /* 802.3at */
+ if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) {
+ if (!(POKE_UINT8(((((port->p_power.powertype ==
+ LLDP_DOT3_POWER_8023AT_TYPE1) ?
+ 1 :
+ 0)
+ << 7) |
+ (((port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ?
+ 0 :
+ 1)
+ << 6) |
+ ((port->p_power.source % (1 << 2)) << 4) |
+ ((port->p_power.priority % (1 << 2)) << 0))) &&
+ POKE_UINT16(port->p_power.requested) &&
+ POKE_UINT16(port->p_power.allocated)))
+ goto toobig;
+ }
+ if (!(POKE_END_LLDP_TLV)) goto toobig;
+ }
+#endif
+
+#ifdef ENABLE_LLDPMED
+ if (port->p_med_cap_enabled) {
+ /* LLDP-MED cap */
+ if (port->p_med_cap_enabled & LLDP_MED_CAP_CAP) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(med, sizeof(med)) &&
+ POKE_UINT8(LLDP_TLV_MED_CAP) &&
+ POKE_UINT16(chassis->c_med_cap_available) &&
+ POKE_UINT8(chassis->c_med_type) && POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+
+ /* LLDP-MED inventory */
+# define LLDP_INVENTORY(value, subtype) \
+ if (value) { \
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) && POKE_BYTES(med, sizeof(med)) && \
+ POKE_UINT8(subtype) && \
+ POKE_BYTES(value, (strlen(value) > 32) ? 32 : strlen(value)) && \
+ POKE_END_LLDP_TLV)) \
+ goto toobig; \
+ }
+
+ if (port->p_med_cap_enabled & LLDP_MED_CAP_IV) {
+ LLDP_INVENTORY(chassis->c_med_hw, LLDP_TLV_MED_IV_HW);
+ LLDP_INVENTORY(chassis->c_med_fw, LLDP_TLV_MED_IV_FW);
+ LLDP_INVENTORY(chassis->c_med_sw, LLDP_TLV_MED_IV_SW);
+ LLDP_INVENTORY(chassis->c_med_sn, LLDP_TLV_MED_IV_SN);
+ LLDP_INVENTORY(chassis->c_med_manuf, LLDP_TLV_MED_IV_MANUF);
+ LLDP_INVENTORY(chassis->c_med_model, LLDP_TLV_MED_IV_MODEL);
+ LLDP_INVENTORY(chassis->c_med_asset, LLDP_TLV_MED_IV_ASSET);
+ }
+
+ /* LLDP-MED location */
+ for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) {
+ if (port->p_med_location[i].format == i + 1) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(med, sizeof(med)) &&
+ POKE_UINT8(LLDP_TLV_MED_LOCATION) &&
+ POKE_UINT8(port->p_med_location[i].format) &&
+ POKE_BYTES(port->p_med_location[i].data,
+ port->p_med_location[i].data_len) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+ }
+
+ /* LLDP-MED network policy */
+ for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) {
+ if (port->p_med_policy[i].type == i + 1) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(med, sizeof(med)) &&
+ POKE_UINT8(LLDP_TLV_MED_POLICY) &&
+ POKE_UINT32((
+ ((port->p_med_policy[i].type % (1 << 8))
+ << 24) |
+ ((port->p_med_policy[i].unknown % (1 << 1))
+ << 23) |
+ ((port->p_med_policy[i].tagged % (1 << 1))
+ << 22) |
+ /*((0 %(1<<
+ 1))<<21) |*/
+ ((port->p_med_policy[i].vid % (1 << 12))
+ << 9) |
+ ((port->p_med_policy[i].priority % (1 << 3))
+ << 6) |
+ ((port->p_med_policy[i].dscp % (1 << 6))
+ << 0))) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+ }
+
+ /* LLDP-MED POE-MDI */
+ if ((port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) ||
+ (port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PD)) {
+ int devicetype = 0, source = 0;
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(med, sizeof(med)) &&
+ POKE_UINT8(LLDP_TLV_MED_MDI)))
+ goto toobig;
+ switch (port->p_med_power.devicetype) {
+ case LLDP_MED_POW_TYPE_PSE:
+ devicetype = 0;
+ switch (port->p_med_power.source) {
+ case LLDP_MED_POW_SOURCE_PRIMARY:
+ source = 1;
+ break;
+ case LLDP_MED_POW_SOURCE_BACKUP:
+ source = 2;
+ break;
+ case LLDP_MED_POW_SOURCE_RESERVED:
+ source = 3;
+ break;
+ default:
+ source = 0;
+ break;
+ }
+ break;
+ case LLDP_MED_POW_TYPE_PD:
+ devicetype = 1;
+ switch (port->p_med_power.source) {
+ case LLDP_MED_POW_SOURCE_PSE:
+ source = 1;
+ break;
+ case LLDP_MED_POW_SOURCE_LOCAL:
+ source = 2;
+ break;
+ case LLDP_MED_POW_SOURCE_BOTH:
+ source = 3;
+ break;
+ default:
+ source = 0;
+ break;
+ }
+ break;
+ }
+ if (!(POKE_UINT8((((devicetype % (1 << 2)) << 6) |
+ ((source % (1 << 2)) << 4) |
+ ((port->p_med_power.priority % (1 << 4)) << 0))) &&
+ POKE_UINT16(port->p_med_power.val) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+ }
+#endif
+
+#ifdef ENABLE_CUSTOM
+ TAILQ_FOREACH (custom, &port->p_custom_list, next) {
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+ POKE_BYTES(custom->oui, sizeof(custom->oui)) &&
+ POKE_UINT8(custom->subtype) &&
+ POKE_BYTES(custom->oui_info, custom->oui_info_len) &&
+ POKE_END_LLDP_TLV))
+ goto toobig;
+ }
+#endif
+
+end:
+ /* END */
+ if (!(POKE_START_LLDP_TLV(LLDP_TLV_END) && POKE_END_LLDP_TLV)) goto toobig;
+
+ if (interfaces_send_helper(global, hardware, (char *)packet, pos - packet) ==
+ -1) {
+ log_warn("lldp", "unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(packet);
+ return ENETDOWN;
+ }
+
+ hardware->h_tx_cnt++;
+
+ /* We assume that LLDP frame is the reference */
+ if (!shutdown &&
+ (frame = (struct lldpd_frame *)malloc(sizeof(int) + pos - packet)) !=
+ NULL) {
+ frame->size = pos - packet;
+ memcpy(&frame->frame, packet, frame->size);
+ if ((hardware->h_lport.p_lastframe == NULL) ||
+ (hardware->h_lport.p_lastframe->size != frame->size) ||
+ (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame,
+ frame->size) != 0)) {
+ free(hardware->h_lport.p_lastframe);
+ hardware->h_lport.p_lastframe = frame;
+ hardware->h_lport.p_lastchange = time(NULL);
+ } else
+ free(frame);
+ }
+
+ free(packet);
+ return 0;
+
+toobig:
+ free(packet);
+ if (vlans > 0 && !without_vlans) {
+ /* Retry without VLANs */
+ return _lldp_send(global, hardware, c_id_subtype, c_id, c_id_len,
+ p_id_subtype, p_id, p_id_len, shutdown, 1);
+ }
+ log_info("lldp", "Cannot send LLDP packet for %s, too big message",
+ hardware->h_ifname);
+ return E2BIG;
+}
+
+/* Send a shutdown LLDPDU. */
+int
+lldp_send_shutdown(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ if (hardware->h_lchassis_previous_id == NULL ||
+ hardware->h_lport_previous_id == NULL)
+ return 0;
+ return _lldp_send(global, hardware, hardware->h_lchassis_previous_id_subtype,
+ hardware->h_lchassis_previous_id, hardware->h_lchassis_previous_id_len,
+ hardware->h_lport_previous_id_subtype, hardware->h_lport_previous_id,
+ hardware->h_lport_previous_id_len, 1, 0);
+}
+
+int
+lldp_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ struct lldpd_port *port = &hardware->h_lport;
+ struct lldpd_chassis *chassis = port->p_chassis;
+ int ret;
+
+ /* Check if we have a change. */
+ if (hardware->h_lchassis_previous_id != NULL &&
+ hardware->h_lport_previous_id != NULL &&
+ (hardware->h_lchassis_previous_id_subtype != chassis->c_id_subtype ||
+ hardware->h_lchassis_previous_id_len != chassis->c_id_len ||
+ hardware->h_lport_previous_id_subtype != port->p_id_subtype ||
+ hardware->h_lport_previous_id_len != port->p_id_len ||
+ memcmp(hardware->h_lchassis_previous_id, chassis->c_id,
+ chassis->c_id_len) ||
+ memcmp(hardware->h_lport_previous_id, port->p_id, port->p_id_len))) {
+ log_info("lldp",
+ "MSAP has changed for port %s, sending a shutdown LLDPDU",
+ hardware->h_ifname);
+ if ((ret = lldp_send_shutdown(global, hardware)) != 0) return ret;
+ }
+
+ log_debug("lldp", "send LLDP PDU to %s", hardware->h_ifname);
+
+ if ((ret = _lldp_send(global, hardware, chassis->c_id_subtype, chassis->c_id,
+ chassis->c_id_len, port->p_id_subtype, port->p_id, port->p_id_len, 0,
+ 0)) != 0)
+ return ret;
+
+ /* Record current chassis and port ID */
+ free(hardware->h_lchassis_previous_id);
+ hardware->h_lchassis_previous_id_subtype = chassis->c_id_subtype;
+ hardware->h_lchassis_previous_id_len = chassis->c_id_len;
+ if ((hardware->h_lchassis_previous_id = malloc(chassis->c_id_len)) != NULL)
+ memcpy(hardware->h_lchassis_previous_id, chassis->c_id,
+ chassis->c_id_len);
+ free(hardware->h_lport_previous_id);
+ hardware->h_lport_previous_id_subtype = port->p_id_subtype;
+ hardware->h_lport_previous_id_len = port->p_id_len;
+ if ((hardware->h_lport_previous_id = malloc(port->p_id_len)) != NULL)
+ memcpy(hardware->h_lport_previous_id, port->p_id, port->p_id_len);
+
+ return 0;
+}
+
+#define CHECK_TLV_SIZE(x, name) \
+ do { \
+ if (tlv_size < (x)) { \
+ log_warnx("lldp", name " TLV too short received on %s", hardware->h_ifname); \
+ goto malformed; \
+ } \
+ } while (0)
+#define CHECK_TLV_MAX_SIZE(x, name) \
+ do { \
+ if (tlv_size > (x)) { \
+ log_warnx("lldp", name " TLV too large received on %s", hardware->h_ifname); \
+ goto malformed; \
+ } \
+ } while (0)
+
+int
+lldp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ char lldpaddr[ETHER_ADDR_LEN];
+ const char dot1[] = LLDP_TLV_ORG_DOT1;
+ const char dot3[] = LLDP_TLV_ORG_DOT3;
+ const char med[] = LLDP_TLV_ORG_MED;
+ const char dcbx[] = LLDP_TLV_ORG_DCBX;
+ unsigned char orgid[3];
+ int length, gotend = 0, ttl_received = 0;
+ int tlv_size, tlv_type, tlv_subtype, tlv_count = 0;
+ u_int8_t *pos, *tlv;
+ char *b;
+#ifdef ENABLE_DOT1
+ struct lldpd_vlan *vlan = NULL;
+ int vlan_len;
+ struct lldpd_ppvid *ppvid;
+ struct lldpd_pi *pi = NULL;
+#endif
+ struct lldpd_mgmt *mgmt;
+ int af;
+ u_int8_t addr_str_length, addr_str_buffer[32] = { 0 };
+ u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype;
+ u_int32_t iface_number, iface;
+ int unrecognized;
+#ifdef ENABLE_CUSTOM
+ struct lldpd_custom *custom = NULL;
+#endif
+
+ log_debug("lldp", "receive LLDP PDU on %s", hardware->h_ifname);
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ log_warn("lldp", "failed to allocate remote chassis");
+ return -1;
+ }
+ TAILQ_INIT(&chassis->c_mgmt);
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ log_warn("lldp", "failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+#ifdef ENABLE_DOT1
+ TAILQ_INIT(&port->p_vlans);
+ TAILQ_INIT(&port->p_ppvids);
+ TAILQ_INIT(&port->p_pids);
+#endif
+#ifdef ENABLE_CUSTOM
+ TAILQ_INIT(&port->p_custom_list);
+#endif
+
+ length = s;
+ pos = (u_int8_t *)frame;
+
+ if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t)) {
+ log_warnx("lldp", "too short frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(lldpaddr, ETHER_ADDR_LEN);
+ if (memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_BRIDGE, ETHER_ADDR_LEN) &&
+ memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_NONTPMR_BRIDGE,
+ ETHER_ADDR_LEN) &&
+ memcmp(lldpaddr, (const char[])LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE,
+ ETHER_ADDR_LEN)) {
+ log_info("lldp",
+ "frame not targeted at LLDP multicast address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_DISCARD(ETHER_ADDR_LEN); /* Skip source address */
+ if (PEEK_UINT16 != ETH_P_LLDP) {
+ log_info("lldp", "non LLDP frame received on %s", hardware->h_ifname);
+ goto malformed;
+ }
+
+ while (length && (!gotend)) {
+ if (length < 2) {
+ log_warnx("lldp", "tlv header too short received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ tlv_size = PEEK_UINT16;
+ tlv_type = tlv_size >> 9;
+ tlv_size = tlv_size & 0x1ff;
+ (void)PEEK_SAVE(tlv);
+ if (length < tlv_size) {
+ log_warnx("lldp", "frame too short for tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ /* Check order for mandatory TLVs */
+ tlv_count++;
+ switch (tlv_type) {
+ case LLDP_TLV_CHASSIS_ID:
+ if (tlv_count != 1) {
+ log_warnx("lldp",
+ "Chassis ID TLV should be first on %s, but it is on position %d",
+ hardware->h_ifname, tlv_count);
+ goto malformed;
+ }
+ break;
+ case LLDP_TLV_PORT_ID:
+ if (tlv_count != 2) {
+ log_warnx("lldp",
+ "Port ID TLV should be second on %s, but it is on position %d",
+ hardware->h_ifname, tlv_count);
+ goto malformed;
+ }
+ break;
+ case LLDP_TLV_TTL:
+ if (tlv_count != 3) {
+ log_warnx("lldp",
+ "TTL TLV should be third on %s, but it is on position %d",
+ hardware->h_ifname, tlv_count);
+ goto malformed;
+ }
+ break;
+ }
+
+ switch (tlv_type) {
+ case LLDP_TLV_END:
+ if (tlv_size != 0) {
+ log_warnx("lldp",
+ "lldp end received with size not null on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (length)
+ log_debug("lldp", "extra data after lldp end on %s",
+ hardware->h_ifname);
+ gotend = 1;
+ break;
+ case LLDP_TLV_CHASSIS_ID:
+ case LLDP_TLV_PORT_ID:
+ CHECK_TLV_SIZE(2, "Port/Chassis Id");
+ CHECK_TLV_MAX_SIZE(256, "Port/Chassis Id");
+ tlv_subtype = PEEK_UINT8;
+ if ((tlv_subtype == 0) || (tlv_subtype > 7)) {
+ log_warnx("lldp",
+ "unknown subtype for tlv id received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) {
+ log_warn("lldp",
+ "unable to allocate memory for id tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(b, tlv_size - 1);
+ if (tlv_type == LLDP_TLV_PORT_ID) {
+ if (port->p_id != NULL) {
+ log_warnx("lldp",
+ "Port ID TLV received twice on %s",
+ hardware->h_ifname);
+ free(b);
+ goto malformed;
+ }
+ port->p_id_subtype = tlv_subtype;
+ port->p_id = b;
+ port->p_id_len = tlv_size - 1;
+ } else {
+ if (chassis->c_id != NULL) {
+ log_warnx("lldp",
+ "Chassis ID TLV received twice on %s",
+ hardware->h_ifname);
+ free(b);
+ goto malformed;
+ }
+ chassis->c_id_subtype = tlv_subtype;
+ chassis->c_id = b;
+ chassis->c_id_len = tlv_size - 1;
+ }
+ break;
+ case LLDP_TLV_TTL:
+ if (ttl_received) {
+ log_warnx("lldp", "TTL TLV received twice on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ CHECK_TLV_SIZE(2, "TTL");
+ port->p_ttl = PEEK_UINT16;
+ ttl_received = 1;
+ break;
+ case LLDP_TLV_PORT_DESCR:
+ case LLDP_TLV_SYSTEM_NAME:
+ case LLDP_TLV_SYSTEM_DESCR:
+ if (tlv_size < 1) {
+ log_debug("lldp", "empty tlv received on %s",
+ hardware->h_ifname);
+ break;
+ }
+ if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) {
+ log_warn("lldp",
+ "unable to allocate memory for string tlv "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(b, tlv_size);
+ switch (tlv_type) {
+ case LLDP_TLV_PORT_DESCR:
+ free(port->p_descr);
+ port->p_descr = b;
+ break;
+ case LLDP_TLV_SYSTEM_NAME:
+ free(chassis->c_name);
+ chassis->c_name = b;
+ break;
+ case LLDP_TLV_SYSTEM_DESCR:
+ free(chassis->c_descr);
+ chassis->c_descr = b;
+ break;
+ default:
+ /* unreachable */
+ free(b);
+ break;
+ }
+ break;
+ case LLDP_TLV_SYSTEM_CAP:
+ CHECK_TLV_SIZE(4, "System capabilities");
+ chassis->c_cap_available = PEEK_UINT16;
+ chassis->c_cap_enabled = PEEK_UINT16;
+ break;
+ case LLDP_TLV_MGMT_ADDR:
+ CHECK_TLV_SIZE(1, "Management address");
+ addr_str_length = PEEK_UINT8;
+ if (addr_str_length > sizeof(addr_str_buffer)) {
+ log_warnx("lldp", "too large management address on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ CHECK_TLV_SIZE(1 + addr_str_length, "Management address");
+ PEEK_BYTES(addr_str_buffer, addr_str_length);
+ addr_length = addr_str_length - 1;
+ addr_family = addr_str_buffer[0];
+ addr_ptr = &addr_str_buffer[1];
+ CHECK_TLV_SIZE(1 + addr_str_length + 5, "Management address");
+ iface_subtype = PEEK_UINT8;
+ iface_number = PEEK_UINT32;
+
+ af = lldpd_af_from_lldp_proto(addr_family);
+ if (af == LLDPD_AF_UNSPEC) break;
+ if (iface_subtype == LLDP_MGMT_IFACE_IFINDEX)
+ iface = iface_number;
+ else
+ iface = 0;
+ mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface);
+ if (mgmt == NULL) {
+ if (errno == ENOMEM)
+ log_warn("lldp",
+ "unable to allocate memory "
+ "for management address");
+ else
+ log_warn("lldp",
+ "too large management address "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
+ break;
+ case LLDP_TLV_ORG:
+ CHECK_TLV_SIZE(1 + (int)sizeof(orgid), "Organisational");
+ PEEK_BYTES(orgid, sizeof(orgid));
+ unrecognized = 0;
+ tlv_subtype = PEEK_UINT8;
+ if (memcmp(dot1, orgid, sizeof(orgid)) == 0) {
+#ifndef ENABLE_DOT1
+ unrecognized = 1;
+#else
+ /* Dot1 */
+ switch (tlv_subtype) {
+ case LLDP_TLV_DOT1_VLANNAME:
+ CHECK_TLV_SIZE(7, "VLAN");
+ if ((vlan = (struct lldpd_vlan *)calloc(1,
+ sizeof(struct lldpd_vlan))) == NULL) {
+ log_warn("lldp",
+ "unable to alloc vlan "
+ "structure for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ vlan->v_vid = PEEK_UINT16;
+ vlan_len = PEEK_UINT8;
+ CHECK_TLV_SIZE(7 + vlan_len, "VLAN");
+ if ((vlan->v_name = (char *)calloc(1,
+ vlan_len + 1)) == NULL) {
+ log_warn("lldp",
+ "unable to alloc vlan name for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(vlan->v_name, vlan_len);
+ TAILQ_INSERT_TAIL(&port->p_vlans, vlan,
+ v_entries);
+ vlan = NULL;
+ break;
+ case LLDP_TLV_DOT1_PVID:
+ CHECK_TLV_SIZE(6, "PVID");
+ port->p_pvid = PEEK_UINT16;
+ break;
+ case LLDP_TLV_DOT1_PPVID:
+ CHECK_TLV_SIZE(7, "PPVID");
+ /* validation needed */
+ /* PPVID has to be unique if more than
+ one PPVID TLVs are received -
+ discard if duplicate */
+ /* if support bit is not set and
+ enabled bit is set - PPVID TLV is
+ considered error and discarded */
+ /* if PPVID > 4096 - bad and discard */
+ if ((ppvid = (struct lldpd_ppvid *)calloc(1,
+ sizeof(struct lldpd_ppvid))) == NULL) {
+ log_warn("lldp",
+ "unable to alloc ppvid "
+ "structure for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ ppvid->p_cap_status = PEEK_UINT8;
+ ppvid->p_ppvid = PEEK_UINT16;
+ TAILQ_INSERT_TAIL(&port->p_ppvids, ppvid,
+ p_entries);
+ break;
+ case LLDP_TLV_DOT1_PI:
+ /* validation needed */
+ /* PI has to be unique if more than
+ one PI TLVs are received - discard
+ if duplicate ?? */
+ CHECK_TLV_SIZE(5, "PI");
+ if ((pi = (struct lldpd_pi *)calloc(1,
+ sizeof(struct lldpd_pi))) == NULL) {
+ log_warn("lldp",
+ "unable to alloc PI "
+ "structure for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ pi->p_pi_len = PEEK_UINT8;
+ CHECK_TLV_SIZE(5 + pi->p_pi_len, "PI");
+ if ((pi->p_pi = (char *)calloc(1,
+ pi->p_pi_len)) == NULL) {
+ log_warn("lldp",
+ "unable to alloc pid name for "
+ "tlv received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(pi->p_pi, pi->p_pi_len);
+ TAILQ_INSERT_TAIL(&port->p_pids, pi, p_entries);
+ pi = NULL;
+ break;
+ default:
+ /* Unknown Dot1 TLV, ignore it */
+ unrecognized = 1;
+ }
+#endif
+ } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) {
+#ifndef ENABLE_DOT3
+ unrecognized = 1;
+#else
+ /* Dot3 */
+ switch (tlv_subtype) {
+ case LLDP_TLV_DOT3_MAC:
+ CHECK_TLV_SIZE(9, "MAC/PHY");
+ port->p_macphy.autoneg_support = PEEK_UINT8;
+ port->p_macphy.autoneg_enabled =
+ (port->p_macphy.autoneg_support & 0x2) >> 1;
+ port->p_macphy.autoneg_support =
+ port->p_macphy.autoneg_support & 0x1;
+ port->p_macphy.autoneg_advertised = PEEK_UINT16;
+ port->p_macphy.mau_type = PEEK_UINT16;
+ break;
+ case LLDP_TLV_DOT3_LA:
+ CHECK_TLV_SIZE(9, "Link aggregation");
+ PEEK_DISCARD_UINT8;
+ port->p_aggregid = PEEK_UINT32;
+ break;
+ case LLDP_TLV_DOT3_MFS:
+ CHECK_TLV_SIZE(6, "MFS");
+ port->p_mfs = PEEK_UINT16;
+ break;
+ case LLDP_TLV_DOT3_POWER:
+ CHECK_TLV_SIZE(7, "Power");
+ port->p_power.devicetype = PEEK_UINT8;
+ port->p_power.supported =
+ (port->p_power.devicetype & 0x2) >> 1;
+ port->p_power.enabled =
+ (port->p_power.devicetype & 0x4) >> 2;
+ port->p_power.paircontrol =
+ (port->p_power.devicetype & 0x8) >> 3;
+ port->p_power.devicetype =
+ (port->p_power.devicetype & 0x1) ?
+ LLDP_DOT3_POWER_PSE :
+ LLDP_DOT3_POWER_PD;
+ port->p_power.pairs = PEEK_UINT8;
+ port->p_power.class = PEEK_UINT8;
+ /* 802.3at? */
+ if (tlv_size >= 12) {
+ port->p_power.powertype = PEEK_UINT8;
+ port->p_power.source =
+ (port->p_power.powertype &
+ (1 << 5 | 1 << 4)) >>
+ 4;
+ port->p_power.priority =
+ (port->p_power.powertype &
+ (1 << 1 | 1 << 0));
+ port->p_power.powertype =
+ (port->p_power.powertype &
+ (1 << 7)) ?
+ LLDP_DOT3_POWER_8023AT_TYPE1 :
+ LLDP_DOT3_POWER_8023AT_TYPE2;
+ port->p_power.requested = PEEK_UINT16;
+ port->p_power.allocated = PEEK_UINT16;
+ } else
+ port->p_power.powertype =
+ LLDP_DOT3_POWER_8023AT_OFF;
+ /* 802.3bt? */
+ if (tlv_size >= 29) {
+ port->p_power.requested_a = PEEK_UINT16;
+ port->p_power.requested_b = PEEK_UINT16;
+ port->p_power.allocated_a = PEEK_UINT16;
+ port->p_power.allocated_b = PEEK_UINT16;
+ port->p_power.pse_status = PEEK_UINT16;
+ port->p_power.pd_status =
+ (port->p_power.pse_status &
+ (1 << 13 | 1 << 12)) >>
+ 12;
+ port->p_power.pse_pairs_ext =
+ (port->p_power.pse_status &
+ (1 << 11 | 1 << 10)) >>
+ 10;
+ port->p_power.class_a =
+ (port->p_power.pse_status &
+ (1 << 9 | 1 << 8 | 1 << 7)) >>
+ 7;
+ port->p_power.class_b =
+ (port->p_power.pse_status &
+ (1 << 6 | 1 << 5 | 1 << 4)) >>
+ 4;
+ port->p_power.class_ext =
+ (port->p_power.pse_status & 0xf);
+ port->p_power.pse_status =
+ (port->p_power.pse_status &
+ (1 << 15 | 1 << 14)) >>
+ 14;
+ port->p_power.type_ext = PEEK_UINT8;
+ port->p_power.pd_load =
+ (port->p_power.type_ext & 0x1);
+ port->p_power.type_ext =
+ ((port->p_power.type_ext &
+ (1 << 3 | 1 << 2 | 1 << 1)) +
+ 1);
+ port->p_power.pse_max = PEEK_UINT16;
+ } else {
+ port->p_power.type_ext =
+ LLDP_DOT3_POWER_8023BT_OFF;
+ }
+ break;
+ default:
+ /* Unknown Dot3 TLV, ignore it */
+ unrecognized = 1;
+ }
+#endif
+ } else if (memcmp(med, orgid, sizeof(orgid)) == 0) {
+ /* LLDP-MED */
+#ifndef ENABLE_LLDPMED
+ unrecognized = 1;
+#else
+ u_int32_t policy;
+ unsigned loctype;
+ unsigned power;
+
+ switch (tlv_subtype) {
+ case LLDP_TLV_MED_CAP:
+ CHECK_TLV_SIZE(7, "LLDP-MED capabilities");
+ chassis->c_med_cap_available = PEEK_UINT16;
+ chassis->c_med_type = PEEK_UINT8;
+ port->p_med_cap_enabled |= LLDP_MED_CAP_CAP;
+ break;
+ case LLDP_TLV_MED_POLICY:
+ CHECK_TLV_SIZE(8, "LLDP-MED policy");
+ policy = PEEK_UINT32;
+ if (((policy >> 24) < 1) ||
+ ((policy >> 24) > LLDP_MED_APPTYPE_LAST)) {
+ log_info("lldp",
+ "unknown policy field %d "
+ "received on %s",
+ policy, hardware->h_ifname);
+ break;
+ }
+ port->p_med_policy[(policy >> 24) - 1].type =
+ (policy >> 24);
+ port->p_med_policy[(policy >> 24) - 1].unknown =
+ ((policy & 0x800000) != 0);
+ port->p_med_policy[(policy >> 24) - 1].tagged =
+ ((policy & 0x400000) != 0);
+ port->p_med_policy[(policy >> 24) - 1].vid =
+ (policy & 0x001FFE00) >> 9;
+ port->p_med_policy[(policy >> 24) - 1]
+ .priority = (policy & 0x1C0) >> 6;
+ port->p_med_policy[(policy >> 24) - 1].dscp =
+ policy & 0x3F;
+ port->p_med_cap_enabled |= LLDP_MED_CAP_POLICY;
+ break;
+ case LLDP_TLV_MED_LOCATION:
+ CHECK_TLV_SIZE(5, "LLDP-MED Location");
+ loctype = PEEK_UINT8;
+ if ((loctype < 1) ||
+ (loctype > LLDP_MED_LOCFORMAT_LAST)) {
+ log_info("lldp",
+ "unknown location type "
+ "received on %s",
+ hardware->h_ifname);
+ break;
+ }
+ free(port->p_med_location[loctype - 1].data);
+ if ((port->p_med_location[loctype - 1].data =
+ (char *)malloc(tlv_size - 5)) ==
+ NULL) {
+ log_warn("lldp",
+ "unable to allocate memory "
+ "for LLDP-MED location for "
+ "frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(
+ port->p_med_location[loctype - 1].data,
+ tlv_size - 5);
+ port->p_med_location[loctype - 1].data_len =
+ tlv_size - 5;
+ port->p_med_location[loctype - 1].format =
+ loctype;
+ port->p_med_cap_enabled |=
+ LLDP_MED_CAP_LOCATION;
+ break;
+ case LLDP_TLV_MED_MDI:
+ CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI");
+ power = PEEK_UINT8;
+ switch (power & 0xC0) {
+ case 0x0:
+ port->p_med_power.devicetype =
+ LLDP_MED_POW_TYPE_PSE;
+ port->p_med_cap_enabled |=
+ LLDP_MED_CAP_MDI_PSE;
+ switch (power & 0x30) {
+ case 0x0:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_UNKNOWN;
+ break;
+ case 0x10:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_PRIMARY;
+ break;
+ case 0x20:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_BACKUP;
+ break;
+ default:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_RESERVED;
+ }
+ break;
+ case 0x40:
+ port->p_med_power.devicetype =
+ LLDP_MED_POW_TYPE_PD;
+ port->p_med_cap_enabled |=
+ LLDP_MED_CAP_MDI_PD;
+ switch (power & 0x30) {
+ case 0x0:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_UNKNOWN;
+ break;
+ case 0x10:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_PSE;
+ break;
+ case 0x20:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_LOCAL;
+ break;
+ default:
+ port->p_med_power.source =
+ LLDP_MED_POW_SOURCE_BOTH;
+ }
+ break;
+ default:
+ port->p_med_power.devicetype =
+ LLDP_MED_POW_TYPE_RESERVED;
+ }
+ if ((power & 0x0F) > LLDP_MED_POW_PRIO_LOW)
+ port->p_med_power.priority =
+ LLDP_MED_POW_PRIO_UNKNOWN;
+ else
+ port->p_med_power.priority =
+ power & 0x0F;
+ port->p_med_power.val = PEEK_UINT16;
+ break;
+ case LLDP_TLV_MED_IV_HW:
+ case LLDP_TLV_MED_IV_SW:
+ case LLDP_TLV_MED_IV_FW:
+ case LLDP_TLV_MED_IV_SN:
+ case LLDP_TLV_MED_IV_MANUF:
+ case LLDP_TLV_MED_IV_MODEL:
+ case LLDP_TLV_MED_IV_ASSET:
+ if (tlv_size <= 4)
+ b = NULL;
+ else {
+ if ((b = (char *)malloc(
+ tlv_size - 3)) == NULL) {
+ log_warn("lldp",
+ "unable to allocate "
+ "memory for LLDP-MED "
+ "inventory for frame "
+ "received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(b, tlv_size - 4);
+ b[tlv_size - 4] = '\0';
+ }
+ switch (tlv_subtype) {
+ case LLDP_TLV_MED_IV_HW:
+ free(chassis->c_med_hw);
+ chassis->c_med_hw = b;
+ break;
+ case LLDP_TLV_MED_IV_FW:
+ free(chassis->c_med_fw);
+ chassis->c_med_fw = b;
+ break;
+ case LLDP_TLV_MED_IV_SW:
+ free(chassis->c_med_sw);
+ chassis->c_med_sw = b;
+ break;
+ case LLDP_TLV_MED_IV_SN:
+ free(chassis->c_med_sn);
+ chassis->c_med_sn = b;
+ break;
+ case LLDP_TLV_MED_IV_MANUF:
+ free(chassis->c_med_manuf);
+ chassis->c_med_manuf = b;
+ break;
+ case LLDP_TLV_MED_IV_MODEL:
+ free(chassis->c_med_model);
+ chassis->c_med_model = b;
+ break;
+ case LLDP_TLV_MED_IV_ASSET:
+ free(chassis->c_med_asset);
+ chassis->c_med_asset = b;
+ break;
+ default:
+ /* unreachable */
+ free(b);
+ break;
+ }
+ port->p_med_cap_enabled |= LLDP_MED_CAP_IV;
+ break;
+ default:
+ /* Unknown LLDP MED, ignore it */
+ hardware->h_rx_unrecognized_cnt++;
+ }
+#endif /* ENABLE_LLDPMED */
+ } else if (memcmp(dcbx, orgid, sizeof(orgid)) == 0) {
+ log_debug("lldp",
+ "unsupported DCBX tlv received on %s - ignore",
+ hardware->h_ifname);
+ unrecognized = 1;
+ } else {
+ log_debug("lldp",
+ "unknown org tlv [%02x:%02x:%02x] received on %s",
+ orgid[0], orgid[1], orgid[2], hardware->h_ifname);
+ unrecognized = 1;
+ }
+ if (unrecognized) {
+ hardware->h_rx_unrecognized_cnt++;
+#ifdef ENABLE_CUSTOM
+ custom = (struct lldpd_custom *)calloc(1,
+ sizeof(struct lldpd_custom));
+ if (!custom) {
+ log_warn("lldp",
+ "unable to allocate memory for custom TLV");
+ goto malformed;
+ }
+ custom->oui_info_len = tlv_size > 4 ? tlv_size - 4 : 0;
+ memcpy(custom->oui, orgid, sizeof(custom->oui));
+ custom->subtype = tlv_subtype;
+ if (custom->oui_info_len > 0) {
+ custom->oui_info = malloc(custom->oui_info_len);
+ if (!custom->oui_info) {
+ log_warn("lldp",
+ "unable to allocate memory for custom TLV data");
+ goto malformed;
+ }
+ PEEK_BYTES(custom->oui_info,
+ custom->oui_info_len);
+ }
+ TAILQ_INSERT_TAIL(&port->p_custom_list, custom, next);
+ custom = NULL;
+#endif
+ }
+ break;
+ default:
+ log_warnx("lldp", "unknown tlv (%d) received on %s", tlv_type,
+ hardware->h_ifname);
+ hardware->h_rx_unrecognized_cnt++;
+ break;
+ }
+ if (pos > tlv + tlv_size) {
+ log_warnx("lldp", "BUG: already past TLV!");
+ goto malformed;
+ }
+ PEEK_DISCARD(tlv + tlv_size - pos);
+ }
+
+ /* Some random check */
+ if ((chassis->c_id == NULL) || (port->p_id == NULL) || (!ttl_received) ||
+ (gotend == 0)) {
+ log_warnx("lldp",
+ "some mandatory tlv are missing for frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+malformed:
+#ifdef ENABLE_CUSTOM
+ free(custom);
+#endif
+#ifdef ENABLE_DOT1
+ free(vlan);
+ free(pi);
+#endif
+ lldpd_chassis_cleanup(chassis, 1);
+ lldpd_port_cleanup(port, 1);
+ free(port);
+ return -1;
+}
diff --git a/src/daemon/protocols/sonmp.c b/src/daemon/protocols/sonmp.c
new file mode 100644
index 0000000..34ebcd7
--- /dev/null
+++ b/src/daemon/protocols/sonmp.c
@@ -0,0 +1,410 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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 "../frame.h"
+
+#ifdef ENABLE_SONMP
+
+# include <stdio.h>
+# include <unistd.h>
+# include <errno.h>
+# include <arpa/inet.h>
+
+static struct sonmp_chassis sonmp_chassis_types[] = {
+ { 1, "unknown (via SONMP)" },
+ { 2, "Nortel 3000" },
+ { 3, "Nortel 3030" },
+ { 4, "Nortel 2310" },
+ { 5, "Nortel 2810" },
+ { 6, "Nortel 2912" },
+ { 7, "Nortel 2914" },
+ { 8, "Nortel 271x" },
+ { 9, "Nortel 2813" },
+ { 10, "Nortel 2814" },
+ { 11, "Nortel 2915" },
+ { 12, "Nortel 5000" },
+ { 13, "Nortel 2813SA" },
+ { 14, "Nortel 2814SA" },
+ { 15, "Nortel 810M" },
+ { 16, "Nortel EtherCell" },
+ { 17, "Nortel 5005" },
+ { 18, "Alcatel Ethernet workgroup conc." },
+ { 20, "Nortel 2715SA" },
+ { 21, "Nortel 2486" },
+ { 22, "Nortel 28000 series" },
+ { 23, "Nortel 23000 series" },
+ { 24, "Nortel 5DN00x series" },
+ { 25, "BayStack Ethernet" },
+ { 26, "Nortel 23100 series" },
+ { 27, "Nortel 100Base-T Hub" },
+ { 28, "Nortel 3000 Fast Ethernet" },
+ { 29, "Nortel Orion switch" },
+ { 30, "unknown" },
+ { 31, "Nortel DDS " },
+ { 32, "Nortel Centillion" },
+ { 33, "Nortel Centillion" },
+ { 34, "Nortel Centillion" },
+ { 35, "BayStack 301" },
+ { 36, "BayStack TokenRing Hub" },
+ { 37, "Nortel FVC Multimedia Switch" },
+ { 38, "Nortel Switch Node" },
+ { 39, "BayStack 302 Switch" },
+ { 40, "BayStack 350 Switch" },
+ { 41, "BayStack 150 Ethernet Hub" },
+ { 42, "Nortel Centillion 50N switch" },
+ { 43, "Nortel Centillion 50T switch" },
+ { 44, "BayStack 303 and 304 Switches" },
+ { 45, "BayStack 200 Ethernet Hub" },
+ { 46, "BayStack 250 10/100 Ethernet Hub" },
+ { 48, "BayStack 450 10/100/1000 Switches" },
+ { 49, "BayStack 410 10/100 Switches" },
+ { 50, "Nortel Ethernet Routing 1200 L3 Switch" },
+ { 51, "Nortel Ethernet Routing 1250 L3 Switch" },
+ { 52, "Nortel Ethernet Routing 1100 L3 Switch" },
+ { 53, "Nortel Ethernet Routing 1150 L3 Switch" },
+ { 54, "Nortel Ethernet Routing 1050 L3 Switch" },
+ { 55, "Nortel Ethernet Routing 1051 L3 Switch" },
+ { 56, "Nortel Ethernet Routing 8610 L3 Switch" },
+ { 57, "Nortel Ethernet Routing 8606 L3 Switch" },
+ { 58, "Nortel Ethernet Routing Switch 8010" },
+ { 59, "Nortel Ethernet Routing Switch 8006" },
+ { 60, "BayStack 670 wireless access point" },
+ { 61, "Nortel Ethernet Routing Switch 740 " },
+ { 62, "Nortel Ethernet Routing Switch 750 " },
+ { 63, "Nortel Ethernet Routing Switch 790" },
+ { 64, "Nortel Business Policy Switch 2000 10/100 Switches" },
+ { 65, "Nortel Ethernet Routing 8110 L2 Switch" },
+ { 66, "Nortel Ethernet Routing 8106 L2 Switch" },
+ { 67, "BayStack 3580 Gig Switch" },
+ { 68, "BayStack 10 Power Supply Unit" },
+ { 69, "BayStack 420 10/100 Switch" },
+ { 70, "OPTera Metro 1200 Ethernet Service Module" },
+ { 71, "Nortel Ethernet Routing Switch 8010co" },
+ { 72, "Nortel Ethernet Routing 8610co L3 switch" },
+ { 73, "Nortel Ethernet Routing 8110co L2 switch" },
+ { 74, "Nortel Ethernet Routing 8003" },
+ { 75, "Nortel Ethernet Routing 8603 L3 switch" },
+ { 76, "Nortel Ethernet Routing 8103 L2 switch" },
+ { 77, "BayStack 380 10/100/1000 Switch" },
+ { 78, "Nortel Ethernet Switch 470-48T" },
+ { 79, "OPTera Metro 1450 Ethernet Service Module" },
+ { 80, "OPTera Metro 1400 Ethernet Service Module" },
+ { 81, "Alteon Switch Family" },
+ { 82, "Ethernet Switch 460-24T-PWR" },
+ { 83, "OPTera Metro 8010 OPM L2 Switch" },
+ { 84, "OPTera Metro 8010co OPM L2 Switch" },
+ { 85, "OPTera Metro 8006 OPM L2 Switch" },
+ { 86, "OPTera Metro 8003 OPM L2 Switch" },
+ { 87, "Alteon 180e" },
+ { 88, "Alteon AD3" },
+ { 89, "Alteon 184" },
+ { 90, "Alteon AD4" },
+ { 91, "Nortel Ethernet Routing 1424 L3 switch" },
+ { 92, "Nortel Ethernet Routing 1648 L3 switch" },
+ { 93, "Nortel Ethernet Routing 1612 L3 switch" },
+ { 94, "Nortel Ethernet Routing 1624 L3 switch " },
+ { 95, "BayStack 380-24F Fiber 1000 Switch" },
+ { 96, "Nortel Ethernet Routing Switch 5510-24T" },
+ { 97, "Nortel Ethernet Routing Switch 5510-48T" },
+ { 98, "Nortel Ethernet Switch 470-24T" },
+ { 99, "Nortel Networks Wireless LAN Access Point 2220" },
+ { 100, "Ethernet Routing RBS 2402 L3 switch" },
+ { 101, "Alteon Application Switch 2424 " },
+ { 102, "Alteon Application Switch 2224 " },
+ { 103, "Alteon Application Switch 2208 " },
+ { 104, "Alteon Application Switch 2216" },
+ { 105, "Alteon Application Switch 3408" },
+ { 106, "Alteon Application Switch 3416" },
+ { 107, "Nortel Networks Wireless LAN SecuritySwitch 2250" },
+ { 108, "Ethernet Switch 425-48T" },
+ { 109, "Ethernet Switch 425-24T" },
+ { 110, "Nortel Networks Wireless LAN Access Point 2221" },
+ { 111, "Nortel Metro Ethernet Service Unit 24-T SPF switch" },
+ { 112, "Nortel Metro Ethernet Service Unit 24-T LX DC switch" },
+ { 113, "Nortel Ethernet Routing Switch 8300 10-slot chassis" },
+ { 114, "Nortel Ethernet Routing Switch 8300 6-slot chassis" },
+ { 115, "Nortel Ethernet Routing Switch 5520-24T-PWR" },
+ { 116, "Nortel Ethernet Routing Switch 5520-48T-PWR" },
+ { 117, "Nortel Networks VPN Gateway 3050" },
+ { 118, "Alteon SSL 310 10/100" },
+ { 119, "Alteon SSL 310 10/100 Fiber" },
+ { 120, "Alteon SSL 310 10/100 FIPS" },
+ { 121, "Alteon SSL 410 10/100/1000" },
+ { 122, "Alteon SSL 410 10/100/1000 Fiber" },
+ { 123, "Alteon Application Switch 2424-SSL" },
+ { 124, "Nortel Ethernet Switch 325-24T" },
+ { 125, "Nortel Ethernet Switch 325-24G" },
+ { 126, "Nortel Networks Wireless LAN Access Point 2225" },
+ { 127, "Nortel Networks Wireless LAN SecuritySwitch 2270" },
+ { 128, "Nortel 24-port Ethernet Switch 470-24T-PWR" },
+ { 129, "Nortel 48-port Ethernet Switch 470-48T-PWR" },
+ { 130, "Nortel Ethernet Routing Switch 5530-24TFD" },
+ { 131, "Nortel Ethernet Switch 3510-24T" },
+ { 132, "Nortel Metro Ethernet Service Unit 12G AC L3 switch" },
+ { 133, "Nortel Metro Ethernet Service Unit 12G DC L3 switch" },
+ { 134, "Nortel Secure Access Switch" },
+ { 135, "Networks VPN Gateway 3070" },
+ { 136, "OPTera Metro 3500" },
+ { 137, "SMB BES 1010 24T" },
+ { 138, "SMB BES 1010 48T" },
+ { 139, "SMB BES 1020 24T PWR" },
+ { 140, "SMB BES 1020 48T PWR" },
+ { 141, "SMB BES 2010 24T" },
+ { 142, "SMB BES 2010 48T" },
+ { 143, "SMB BES 2020 24T PWR" },
+ { 144, "SMB BES 2020 48T PWR" },
+ { 145, "SMB BES 110 24T" },
+ { 146, "SMB BES 110 48T" },
+ { 147, "SMB BES 120 24T PWR" },
+ { 148, "SMB BES 120 48T PWR" },
+ { 149, "SMB BES 210 24T" },
+ { 150, "SMB BES 210 48T" },
+ { 151, "SMB BES 220 24T PWR" },
+ { 152, "SMB BES 220 48T PWR" },
+ { 153, "OME 6500" },
+ { 0, "unknown (via SONMP)" },
+};
+
+int
+sonmp_send(struct lldpd *global, struct lldpd_hardware *hardware)
+{
+ const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
+ const u_int8_t llcorg[] = LLC_ORG_NORTEL;
+ struct lldpd_chassis *chassis;
+ struct lldpd_mgmt *mgmt;
+ u_int8_t *packet, *pos, *pos_pid, *end;
+ int length;
+ struct in_addr address;
+
+ log_debug("sonmp", "send SONMP PDU to %s", hardware->h_ifname);
+
+ chassis = hardware->h_lport.p_chassis;
+ length = hardware->h_mtu;
+ if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM;
+ pos = packet;
+
+ /* Ethernet header */
+ if (!(
+ /* SONMP multicast address as target */
+ POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+ /* Source MAC addresss */
+ POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
+ /* SONMP frame is of fixed size */
+ POKE_UINT16(SONMP_SIZE)))
+ goto toobig;
+
+ /* LLC header */
+ if (!(
+ /* DSAP and SSAP */
+ POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
+ /* Control field */
+ POKE_UINT8(0x03) &&
+ /* ORG */
+ POKE_BYTES(llcorg, sizeof(llcorg)) &&
+ POKE_SAVE(pos_pid) && /* We will modify PID later to
+ create a new frame */
+ POKE_UINT16(LLC_PID_SONMP_HELLO)))
+ goto toobig;
+
+ address.s_addr = htonl(INADDR_ANY);
+ TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) {
+ if (mgmt->m_family == LLDPD_AF_IPV4) {
+ address.s_addr = mgmt->m_addr.inet.s_addr;
+ }
+ break;
+ }
+
+ /* SONMP */
+ if (!(
+ /* Our IP address */
+ POKE_BYTES(&address, sizeof(struct in_addr)) &&
+ /* Segment on three bytes, we don't have slots, so we
+ skip the first two bytes */
+ POKE_UINT16(0) && 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_UINT8(1) && /* Links: Dunno what it is */
+ POKE_SAVE(end)))
+ goto toobig;
+
+ if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) ==
+ -1) {
+ log_warn("sonmp", "unable to send packet on real device for %s",
+ hardware->h_ifname);
+ free(packet);
+ return ENETDOWN;
+ }
+
+ POKE_RESTORE(pos_pid); /* Modify LLC PID */
+ (void)POKE_UINT16(LLC_PID_SONMP_FLATNET);
+ POKE_RESTORE(packet); /* Go to the beginning */
+ PEEK_DISCARD(ETHER_ADDR_LEN - 1); /* Modify the last byte of the MAC address */
+ (void)POKE_UINT8(1);
+
+ if (interfaces_send_helper(global, hardware, (char *)packet, end - packet) ==
+ -1) {
+ log_warn("sonmp",
+ "unable to send second SONMP packet on real device for %s",
+ hardware->h_ifname);
+ free(packet);
+ return ENETDOWN;
+ }
+
+ free(packet);
+ hardware->h_tx_cnt++;
+ return 0;
+toobig:
+ free(packet);
+ return -1;
+}
+
+int
+sonmp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware,
+ struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+{
+ const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
+ struct lldpd_chassis *chassis;
+ struct lldpd_port *port;
+ struct lldpd_mgmt *mgmt;
+ int length, i;
+ u_int8_t *pos;
+ u_int8_t seg[3], rchassis;
+ struct in_addr address;
+
+ log_debug("sonmp", "decode SONMP PDU from %s", hardware->h_ifname);
+
+ if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+ log_warn("sonmp", "failed to allocate remote chassis");
+ return -1;
+ }
+ TAILQ_INIT(&chassis->c_mgmt);
+ if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+ log_warn("sonmp", "failed to allocate remote port");
+ free(chassis);
+ return -1;
+ }
+# ifdef ENABLE_DOT1
+ TAILQ_INIT(&port->p_vlans);
+# endif
+
+ length = s;
+ pos = (u_int8_t *)frame;
+ if (length < SONMP_SIZE + 2 * ETHER_ADDR_LEN + sizeof(u_int16_t)) {
+ log_warnx("sonmp", "too short SONMP frame received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ if (PEEK_CMP(mcastaddr, sizeof(mcastaddr)) != 0)
+ /* There is two multicast address. We just handle only one of
+ * them. */
+ goto malformed;
+ /* We skip to LLC PID */
+ PEEK_DISCARD(ETHER_ADDR_LEN);
+ PEEK_DISCARD_UINT16;
+ PEEK_DISCARD(6);
+ if (PEEK_UINT16 != LLC_PID_SONMP_HELLO) {
+ log_debug("sonmp", "incorrect LLC protocol ID received for SONMP on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_ADDR;
+ if ((chassis->c_id = calloc(1, sizeof(struct in_addr) + 1)) == NULL) {
+ log_warn("sonmp", "unable to allocate memory for chassis id on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ chassis->c_id_len = sizeof(struct in_addr) + 1;
+ chassis->c_id[0] = 1;
+ PEEK_BYTES(&address, sizeof(struct in_addr));
+ memcpy(chassis->c_id + 1, &address, sizeof(struct in_addr));
+ if (asprintf(&chassis->c_name, "%s", inet_ntoa(address)) == -1) {
+ log_warnx("sonmp", "unable to write chassis name for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ PEEK_BYTES(seg, sizeof(seg));
+ rchassis = PEEK_UINT8;
+ for (i = 0; sonmp_chassis_types[i].type != 0; i++) {
+ if (sonmp_chassis_types[i].type == rchassis) break;
+ }
+ if (asprintf(&chassis->c_descr, "%s", sonmp_chassis_types[i].description) ==
+ -1) {
+ log_warnx("sonmp", "unable to write chassis description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, sizeof(struct in_addr), 0);
+ if (mgmt == NULL) {
+ if (errno == ENOMEM)
+ log_warn("sonmp",
+ "unable to allocate memory for management address");
+ else
+ log_warn("sonmp", "too large management address received on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
+ port->p_ttl =
+ cfg ? (cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold) : LLDPD_TTL;
+ port->p_ttl = (port->p_ttl + 999) / 1000;
+
+ port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
+
+ port->p_id_len =
+ asprintf(&port->p_id, "%02x-%02x-%02x", seg[0], seg[1], seg[2]);
+ if (port->p_id_len == -1) {
+ log_warn("sonmp", "unable to allocate memory for port id on %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+
+ /* Port description depend on the number of segments */
+ if ((seg[0] == 0) && (seg[1] == 0)) {
+ if (asprintf(&port->p_descr, "port %d", seg[2]) == -1) {
+ log_warnx("sonmp", "unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ } else if (seg[0] == 0) {
+ if (asprintf(&port->p_descr, "port %d/%d", seg[1], seg[2]) == -1) {
+ log_warnx("sonmp", "unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ } else {
+ if (asprintf(&port->p_descr, "port %x:%x:%x", seg[0], seg[1], seg[2]) ==
+ -1) {
+ log_warnx("sonmp", "unable to write port description for %s",
+ hardware->h_ifname);
+ goto malformed;
+ }
+ }
+ *newchassis = chassis;
+ *newport = port;
+ return 1;
+
+malformed:
+ lldpd_chassis_cleanup(chassis, 1);
+ lldpd_port_cleanup(port, 1);
+ free(port);
+ return -1;
+}
+
+#endif /* ENABLE_SONMP */
diff --git a/src/daemon/protocols/sonmp.h b/src/daemon/protocols/sonmp.h
new file mode 100644
index 0000000..513c4bb
--- /dev/null
+++ b/src/daemon/protocols/sonmp.h
@@ -0,0 +1,42 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _SONMP_H
+#define _SONMP_H
+
+#define SONMP_MULTICAST_ADDR \
+ { \
+ 0x01, 0x00, 0x81, 0x00, 0x01, 0x00 \
+ }
+#define LLC_ORG_NORTEL \
+ { \
+ 0x00, 0x00, 0x81 \
+ }
+#define LLC_PID_SONMP_HELLO 0x01a2
+#define LLC_PID_SONMP_FLATNET 0x01a1
+#define SONMP_SIZE 19
+
+struct sonmp_chassis {
+ int type;
+ const char *description;
+};
+
+#define SONMP_TOPOLOGY_CHANGED 1
+#define SONMP_TOPOLOGY_UNCHANGED 2
+#define SONMP_TOPOLOGY_NEW 3
+
+#endif /* _SONMP_H */