/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * Copyright (c) 2014 Michael Chapman * * 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 #include "lldpd.h" #include #include #include #include #include #include #include "compiler.h" #include "dp-packet.h" #include "packets.h" VLOG_DEFINE_THIS_MODULE(lldp); /* This set of macro are used to parse packets. The current position in buffer * is `pos'. The length of the remaining space in buffer is `length'. There is * no check on boundaries. */ #define PEEK(type, func) \ ( \ memcpy(&type, pos, sizeof type), \ length -= sizeof type, \ pos += sizeof type, \ func(type) \ ) #define PEEK_UINT8 PEEK(types.f_uint8, ) #define PEEK_UINT16 PEEK(types.f_uint16, ntohs) #define PEEK_UINT32 PEEK(types.f_uint32, ntohl) #define PEEK_BYTES(value, bytes) \ do { \ memcpy(value, pos, bytes); \ length -= (bytes); \ pos += (bytes); \ } while (0) #define PEEK_DISCARD(bytes) \ do { \ length -= (bytes); \ pos += (bytes); \ } while (0) #define PEEK_DISCARD_UINT8 PEEK_DISCARD(1) #define PEEK_DISCARD_UINT16 PEEK_DISCARD(2) #define PEEK_DISCARD_UINT32 PEEK_DISCARD(4) #define PEEK_CMP(value, bytes) \ (length -= (bytes), \ pos += (bytes), \ memcmp(pos-bytes, value, bytes)) #define CHECK_TLV_SIZE(x, name) \ do { \ if (tlv_size < (x)) { \ VLOG_WARN(name " TLV too short received on %s", \ hardware->h_ifname); \ goto malformed; \ } \ } while (0) #define PEEK_SAVE(where) (where = pos, 1) static union { uint8_t f_uint8; ovs_be16 f_uint16; ovs_be32 f_uint32; } types; 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 void lldp_tlv_put_u8(struct dp_packet *p, uint8_t x) { dp_packet_put(p, &x, sizeof x); } static void lldp_tlv_put_u16(struct dp_packet *p, uint16_t x) { ovs_be16 nx = htons(x); dp_packet_put(p, &nx, sizeof nx); } static void lldp_tlv_put_u32(struct dp_packet *p, uint32_t x) { ovs_be32 nx = htonl(x); dp_packet_put(p, &nx, sizeof nx); } static void lldp_tlv_put_isid(struct dp_packet *p, uint32_t isid) { uint8_t *data = dp_packet_put_uninit(p, 3); data[0] = isid >> 16; data[1] = isid >> 8; data[2] = isid; } static void lldp_tlv_start(struct dp_packet *p, uint8_t tlv, unsigned int *start) { *start = dp_packet_size(p); lldp_tlv_put_u16(p, tlv << 9); } static void lldp_tlv_end(struct dp_packet *p, unsigned int start) { ovs_be16 *tlv = dp_packet_at_assert(p, start, 2); put_unaligned_be16(tlv, get_unaligned_be16(tlv) | htons((dp_packet_size(p) - (start + 2)) & 0x1ff)); } int lldp_send(struct lldpd *global OVS_UNUSED, struct lldpd_hardware *hardware, struct dp_packet *p) { unsigned int orig_size = dp_packet_size(p); unsigned int start; struct lldpd_port *port; struct lldpd_chassis *chassis; struct lldpd_mgmt *mgmt; const uint8_t avaya[] = LLDP_TLV_ORG_AVAYA; struct lldpd_aa_isid_vlan_maps_tlv *vlan_isid_map; uint8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH]; port = &hardware->h_lport; chassis = port->p_chassis; /* The ethernet header is filled in elsewhere, we must save room for it. */ VLOG_DBG("LLDP PDU send to %s mtu %d incoming", hardware->h_ifname, hardware->h_mtu); /* Chassis ID */ lldp_tlv_start(p, LLDP_TLV_CHASSIS_ID, &start); lldp_tlv_put_u8(p, chassis->c_id_subtype); dp_packet_put(p, chassis->c_id, chassis->c_id_len); lldp_tlv_end(p, start); /* Port ID */ lldp_tlv_start(p, LLDP_TLV_PORT_ID, &start); lldp_tlv_put_u8(p, port->p_id_subtype); dp_packet_put(p, port->p_id, port->p_id_len); lldp_tlv_end(p, start); /* Time to live */ lldp_tlv_start(p, LLDP_TLV_TTL, &start); lldp_tlv_put_u16(p, chassis->c_ttl); lldp_tlv_end(p, start); /* System name */ if (chassis->c_name && *chassis->c_name != '\0') { lldp_tlv_start(p, LLDP_TLV_SYSTEM_NAME, &start); dp_packet_put(p, chassis->c_name, strlen(chassis->c_name)); lldp_tlv_end(p, start); } /* System description (skip it if empty) */ if (chassis->c_descr && *chassis->c_descr != '\0') { lldp_tlv_start(p, LLDP_TLV_SYSTEM_DESCR, &start); dp_packet_put(p, chassis->c_descr, strlen(chassis->c_descr)); lldp_tlv_end(p, start); } /* System capabilities */ lldp_tlv_start(p, LLDP_TLV_SYSTEM_CAP, &start); lldp_tlv_put_u16(p, chassis->c_cap_available); lldp_tlv_put_u16(p, chassis->c_cap_enabled); lldp_tlv_end(p, start); LIST_FOR_EACH (mgmt, m_entries, &chassis->c_mgmt) { lldp_tlv_start(p, LLDP_TLV_MGMT_ADDR, &start); lldp_tlv_put_u8(p, mgmt->m_addrsize + 1); lldp_tlv_put_u8(p, lldpd_af_to_lldp_proto(mgmt->m_family)); dp_packet_put(p, &mgmt->m_addr, mgmt->m_addrsize); /* Interface port type, OID */ if (mgmt->m_iface == 0) { /* We don't know the management interface */ lldp_tlv_put_u8(p, LLDP_MGMT_IFACE_UNKNOWN); lldp_tlv_put_u32(p, 0); } else { /* We have the index of the management interface */ lldp_tlv_put_u8(p, LLDP_MGMT_IFACE_IFINDEX); lldp_tlv_put_u32(p, mgmt->m_iface); } lldp_tlv_put_u8(p, 0); lldp_tlv_end(p, start); } /* Port description */ if (port->p_descr && *port->p_descr != '\0') { lldp_tlv_start(p, LLDP_TLV_PORT_DESCR, &start); dp_packet_put(p, port->p_descr, strlen(port->p_descr)); lldp_tlv_end(p, start); } /* Add Auto Attach tlvs V3.1 to packet. LLDP FA element v3.1 format: TLV Type[127] TLV Length[50 octets] Avaya OUI[00-04-0D] Subtype[11] 7 bits 9 bits 3 octets 1 octet HMAC-SHA Digest Element Type State Mgmt VLAN Rsvd System ID 32 octets 6 bits 6 bits 12 bits 1 octet 10 octets */ /* AA-ELEMENT */ if (port->p_element.type != 0) { u_int16_t aa_element_first_word = 0; u_int16_t aa_element_second_word = 0; u_int16_t aa_element_state = 0; u_int8_t aa_elem_sys_id_first_byte; u_int8_t aa_elem_sys_id_second_byte; /* Link VLAN Tagging Requirements (bit 1), * Automatic Provisioning Mode (bit 2/3) (left to right, 1 based) */ aa_element_state = ((port->p_element.vlan_tagging & 0x1) << 5) | ((port->p_element.auto_prov_mode & 0x3) << 3); /* Element first word should be first 6 most significant bits of * element type, bitwise OR that with the next 6 bits of the state, * bitwise OR with the first 4 bits of mgmt vlan id. * Element type should be LLDP_TLV_AA_ELEM_TYPE_VIRTUAL_SWITCH for * AA client */ aa_element_first_word = (port->p_element.type << 10) | (aa_element_state << 4) | ((port->p_element.mgmt_vlan & 0x0F00)>> 8); /* Element second type should be the first 8 most significant bits * of the remaining 8 bits of mgmt vlan id. */ aa_element_second_word = (port->p_element.mgmt_vlan & 0xFF) << 8; /* System id first byte should be first 3 most significant bits of * connecion type, bitwise OR that with the device state and bitwise * OR that with the first 2 most significant bitsof rsvd (10 bits). */ aa_elem_sys_id_first_byte = ((port->p_element.system_id.conn_type & 0x7) << 5) | ((port->p_element.system_id.rsvd >> 8) & 0x3); /* Second byte should just be the remaining 8 bits of 10 bits rsvd */ aa_elem_sys_id_second_byte = (port->p_element.system_id.rsvd & 0xFF); memset(msg_auth_digest, 0, sizeof msg_auth_digest); lldp_tlv_start(p, LLDP_TLV_ORG, &start); dp_packet_put(p, avaya, sizeof avaya); lldp_tlv_put_u8(p, LLDP_TLV_AA_ELEMENT_SUBTYPE); dp_packet_put(p, msg_auth_digest, sizeof msg_auth_digest); lldp_tlv_put_u16(p, aa_element_first_word); lldp_tlv_put_u16(p, aa_element_second_word); dp_packet_put(p, &port->p_element.system_id.system_mac, sizeof port->p_element.system_id.system_mac); lldp_tlv_put_u8(p, aa_elem_sys_id_first_byte); lldp_tlv_put_u8(p, aa_elem_sys_id_second_byte); dp_packet_put(p, &port->p_element.system_id.rsvd2, sizeof port->p_element.system_id.rsvd2); lldp_tlv_end(p, start); } if (!ovs_list_is_empty(&port->p_isid_vlan_maps)) { memset(msg_auth_digest, 0, sizeof msg_auth_digest); lldp_tlv_start(p, LLDP_TLV_ORG, &start); dp_packet_put(p, avaya, sizeof avaya); lldp_tlv_put_u8(p, LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE); dp_packet_put(p, msg_auth_digest, sizeof msg_auth_digest); LIST_FOR_EACH (vlan_isid_map, m_entries, &hardware->h_lport.p_isid_vlan_maps) { u_int16_t status_vlan_word; status_vlan_word = (vlan_isid_map->isid_vlan_data.status << 12) | vlan_isid_map->isid_vlan_data.vlan; lldp_tlv_put_u16(p, status_vlan_word); lldp_tlv_put_isid(p, vlan_isid_map->isid_vlan_data.isid); } lldp_tlv_end(p, start); } /* END */ lldp_tlv_start(p, LLDP_TLV_END, &start); lldp_tlv_end(p, start); hardware->h_tx_cnt++; const char *lldp = dp_packet_at_assert(p, orig_size, 0); unsigned int lldp_len = dp_packet_size(p) - orig_size; if (!hardware->h_lport.p_lastframe || hardware->h_lport.p_lastframe->size != lldp_len || memcmp(hardware->h_lport.p_lastframe->frame, lldp, lldp_len)) { struct lldpd_frame *frame = xmalloc(sizeof *frame + lldp_len); frame->size = lldp_len; memcpy(frame->frame, lldp, lldp_len); free(hardware->h_lport.p_lastframe); hardware->h_lport.p_lastframe = frame; hardware->h_lport.p_lastchange = time(NULL); } return dp_packet_size(p); } #define CHECK_TLV_MAX_SIZE(x, name) \ do { if (tlv_size > (x)) { \ VLOG_WARN(name " TLV too large received on %s", \ hardware->h_ifname); \ goto malformed; \ } } while (0) int lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, struct lldpd_hardware *hardware, struct lldpd_chassis **newchassis, struct lldpd_port **newport) { struct lldpd_chassis *chassis; struct lldpd_port *port; const struct eth_addr lldpaddr = LLDP_MULTICAST_ADDR; const char dot1[] = LLDP_TLV_ORG_DOT1; const char dot3[] = LLDP_TLV_ORG_DOT3; const char med[] = LLDP_TLV_ORG_MED; const char avaya_oid[] = LLDP_TLV_ORG_AVAYA; const char dcbx[] = LLDP_TLV_ORG_DCBX; char orgid[3]; int length, af; bool gotend = false; bool ttl_received = false; int tlv_size, tlv_type, tlv_subtype, tlv_count = 0; u_int8_t *pos, *tlv; void *b; struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL; u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH]; struct lldpd_mgmt *mgmt; u_int8_t addr_str_length, addr_str_buffer[32]; u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype; u_int32_t iface_number, iface; VLOG_DBG("receive LLDP PDU on %s", hardware->h_ifname); chassis = xzalloc(sizeof *chassis); ovs_list_init(&chassis->c_mgmt); port = xzalloc(sizeof *port); ovs_list_init(&port->p_isid_vlan_maps); length = s; pos = (u_int8_t*) frame; if (length < 2 * ETH_ADDR_LEN + sizeof(u_int16_t)) { VLOG_WARN("too short frame received on %s", hardware->h_ifname); goto malformed; } if (PEEK_CMP(&lldpaddr, ETH_ADDR_LEN) != 0) { VLOG_INFO("frame not targeted at LLDP multicast address " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(ETH_ADDR_LEN); /* Skip source address */ if (PEEK_UINT16 != ETHERTYPE_LLDP) { VLOG_INFO("non LLDP frame received on %s", hardware->h_ifname); goto malformed; } while (length && !gotend) { if (length < 2) { VLOG_WARN("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) { VLOG_WARN("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) { VLOG_WARN("first TLV should be a chassis ID on %s, not %d", hardware->h_ifname, tlv_type); goto malformed; } break; case LLDP_TLV_PORT_ID: if (tlv_count != 2) { VLOG_WARN("second TLV should be a port ID on %s, not %d", hardware->h_ifname, tlv_type); goto malformed; } break; case LLDP_TLV_TTL: if (tlv_count != 3) { VLOG_WARN("third TLV should be a TTL on %s, not %d", hardware->h_ifname, tlv_type); goto malformed; } break; } switch (tlv_type) { case LLDP_TLV_END: if (tlv_size != 0) { VLOG_WARN("lldp end received with size not null on %s", hardware->h_ifname); goto malformed; } if (length) { VLOG_DBG("extra data after lldp end on %s", hardware->h_ifname); } gotend = true; 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) { VLOG_WARN("unknown subtype for tlv id received on %s", hardware->h_ifname); goto malformed; } b = xzalloc(tlv_size - 1); PEEK_BYTES(b, tlv_size - 1); if (tlv_type == LLDP_TLV_PORT_ID) { if (port->p_id != NULL) { VLOG_WARN("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) { VLOG_WARN("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) { VLOG_WARN("TTL TLV received twice on %s", hardware->h_ifname); goto malformed; } CHECK_TLV_SIZE(2, "TTL"); chassis->c_ttl = PEEK_UINT16; ttl_received = true; break; case LLDP_TLV_PORT_DESCR: case LLDP_TLV_SYSTEM_NAME: case LLDP_TLV_SYSTEM_DESCR: if (tlv_size < 1) { VLOG_DBG("empty tlv received on %s", hardware->h_ifname); break; } b = xzalloc(tlv_size + 1); PEEK_BYTES(b, tlv_size); if (tlv_type == LLDP_TLV_PORT_DESCR) { free(port->p_descr); port->p_descr = b; } else if (tlv_type == LLDP_TLV_SYSTEM_NAME) { free(chassis->c_name); chassis->c_name = b; } else { free(chassis->c_descr); chassis->c_descr = b; } 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)) { VLOG_WARN("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; } iface = iface_subtype == LLDP_MGMT_IFACE_IFINDEX ? iface_number : 0; mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface); if (mgmt == NULL) { VLOG_WARN("unable to allocate memory for management address"); goto malformed; } ovs_list_push_back(&chassis->c_mgmt, &mgmt->m_entries); break; case LLDP_TLV_ORG: CHECK_TLV_SIZE(1 + sizeof orgid, "Organisational"); PEEK_BYTES(orgid, sizeof orgid); tlv_subtype = PEEK_UINT8; if (memcmp(dot1, orgid, sizeof orgid) == 0) { hardware->h_rx_unrecognized_cnt++; } else if (memcmp(dot3, orgid, sizeof orgid) == 0) { hardware->h_rx_unrecognized_cnt++; } else if (memcmp(med, orgid, sizeof orgid) == 0) { /* LLDP-MED */ hardware->h_rx_unrecognized_cnt++; } else if (memcmp(avaya_oid, orgid, sizeof orgid) == 0) { u_int32_t aa_element_dword; u_int16_t aa_system_id_word; u_int16_t aa_status_vlan_word; u_int8_t aa_element_state; unsigned short num_mappings; switch(tlv_subtype) { case LLDP_TLV_AA_ELEMENT_SUBTYPE: CHECK_TLV_SIZE(50, "ELEMENT"); PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); aa_element_dword = PEEK_UINT32; /* Type is first 6 most-significant bits of * aa_element_dword */ port->p_element.type = aa_element_dword >> 26; /* State is 6 most significant bits of aa_element_dword */ aa_element_state = (aa_element_dword >> 20) & 0x3F; /* vlan tagging requirement is the bit 1(left to right) * of the 6 bits state (1 based) */ port->p_element.vlan_tagging = (aa_element_state >> 5) & 0x1; /* Automatic provision mode is the bit 2/3(left to right) * of the 6 bits state (1 based) */ port->p_element.auto_prov_mode = (aa_element_state >> 3) & 0x3; /* mgmt_vlan is the 12 bits of aa_element_dword from * bit 12 */ port->p_element.mgmt_vlan = (aa_element_dword >> 8) & 0xFFF; VLOG_INFO("Element type: %X, vlan tagging %X, " "auto prov mode %x, Mgmt vlan: %X", port->p_element.type, port->p_element.vlan_tagging, port->p_element.auto_prov_mode, port->p_element.mgmt_vlan); PEEK_BYTES(&port->p_element.system_id.system_mac, sizeof port->p_element.system_id.system_mac); VLOG_INFO("System mac: "ETH_ADDR_FMT, ETH_ADDR_ARGS(port->p_element.system_id.system_mac)); aa_system_id_word = PEEK_UINT16; port->p_element.system_id.conn_type = aa_system_id_word >> 13; port->p_element.system_id.rsvd = aa_system_id_word & 0x03FF; PEEK_BYTES(&port->p_element.system_id.rsvd2, sizeof port->p_element.system_id.rsvd2); break; case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE: CHECK_TLV_SIZE(36, "ISID_VLAN_ASGNS"); PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); /* Subtract off tlv type and length (2Bytes) + OUI (3B) + * Subtype (1B) + MSG DIGEST (32B). */ num_mappings = tlv_size - 4 - LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH; if (num_mappings % 5 != 0) { VLOG_INFO("malformed vlan-isid mappings tlv received"); goto malformed; } num_mappings /= 5; /* Each mapping is 5 Bytes */ for(; num_mappings > 0; num_mappings--) { uint8_t isid[3]; isid_vlan_map = xzalloc(sizeof *isid_vlan_map); aa_status_vlan_word = PEEK_UINT16; /* Status is first 4 most-significant bits. */ isid_vlan_map->isid_vlan_data.status = aa_status_vlan_word >> 12; /* Vlan is last 12 bits */ isid_vlan_map->isid_vlan_data.vlan = aa_status_vlan_word & 0x0FFF; PEEK_BYTES(isid, 3); isid_vlan_map->isid_vlan_data.isid = (isid[0] << 16) | (isid[1] << 8) | isid[2]; ovs_list_push_back(&port->p_isid_vlan_maps, &isid_vlan_map->m_entries); isid_vlan_map = NULL; } break; default: hardware->h_rx_unrecognized_cnt++; VLOG_INFO("Unrecogised tlv subtype received"); break; } } else if (memcmp(dcbx, orgid, sizeof orgid) == 0) { VLOG_DBG("unsupported DCBX tlv received on %s " "- ignore", hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } else { VLOG_INFO("unknown org tlv [%02x:%02x:%02x] received " "on %s", orgid[0], orgid[1], orgid[2], hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } break; default: VLOG_WARN("unknown tlv (%d) received on %s", tlv_type, hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; goto malformed; } if (pos > tlv + tlv_size) { VLOG_WARN("BUG: already past TLV!"); goto malformed; } PEEK_DISCARD(tlv + tlv_size - pos); } /* Some random check */ if (!chassis->c_id || !port->p_id || !ttl_received || !gotend) { VLOG_WARN("some mandatory tlv are missing for frame received " "on %s", hardware->h_ifname); goto malformed; } *newchassis = chassis; *newport = port; return 1; malformed: lldpd_chassis_cleanup(chassis, true); lldpd_port_cleanup(port, true); free(port); return -1; }