diff options
Diffstat (limited to 'src/daemon/protocols/cdp.c')
-rw-r--r-- | src/daemon/protocols/cdp.c | 711 |
1 files changed, 711 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) */ |