/* * Copyright (c) 2011-2016 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include cdp_main_t cdp_main; #define DEBUG_TLV_DUMP 0 /* 1=> dump TLV's to stdout while processing them */ /* * ported from an unspecified Cisco cdp implementation. * Compute / return in HOST byte order. 0 => good checksum. */ u16 cdp_checksum (void *p, int count) { u32 sum; u16 i, *data; data = p; sum = 0; while (count > 1) { sum += ntohs (*data); data++; count -= 2; } if (count > 0) sum += *(char *) data; while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } i = (i16) sum; return (~i); } /* TLV handler table */ typedef struct { char *name; u32 tlv_id; void *format; void *process; } tlv_handler_t; static tlv_handler_t tlv_handlers[]; /* Display a generic TLV as a set of hex bytes */ static u8 * format_generic_tlv (u8 * s, va_list * va) { cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *); tlv_handler_t *h = &tlv_handlers[t->t]; s = format (s, "%s(%d): %U\n", h->name, t->t, format_hex_bytes, t->v, t->l - sizeof (*t)); return s; } /* Ignore / skip a TLV we don't support */ static cdp_error_t process_generic_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t) { #if DEBUG_TLV_DUMP > 0 fformat (stdout, "%U", format_generic_tlv, t); #endif return CDP_ERROR_NONE; } /* print a text tlv */ static u8 * format_text_tlv (u8 * s, va_list * va) { cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *); tlv_handler_t *h = &tlv_handlers[t->t]; int i; s = format (s, "%s(%d): ", h->name, t->t); if (t->l >= 4) { for (i = 0; i < (t->l - sizeof (*t)); i++) vec_add1 (s, t->v[i]); } vec_add1 (s, '\n'); return s; } #if DEBUG_TLV_DUMP == 0 /* gcc warning be gone */ CLIB_UNUSED (static cdp_error_t process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)); #endif /* process / skip a generic text TLV that we don't support */ static cdp_error_t process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t) { #if DEBUG_TLV_DUMP > 0 fformat (stdout, "%U\n", format_text_tlv, t); #endif return CDP_ERROR_NONE; } /* per-TLV format function definitions */ #define format_unused_tlv format_generic_tlv #define format_device_name_tlv format_text_tlv #define format_address_tlv format_generic_tlv #define format_port_id_tlv format_text_tlv #define format_capabilities_tlv format_generic_tlv #define format_version_tlv format_text_tlv #define format_platform_tlv format_text_tlv #define format_ipprefix_tlv format_generic_tlv #define format_hello_tlv format_generic_tlv #define format_vtp_domain_tlv format_generic_tlv #define format_native_vlan_tlv format_generic_tlv #define format_duplex_tlv format_generic_tlv #define format_appl_vlan_tlv format_generic_tlv #define format_trigger_tlv format_generic_tlv #define format_power_tlv format_generic_tlv #define format_mtu_tlv format_generic_tlv #define format_trust_tlv format_generic_tlv #define format_cos_tlv format_generic_tlv #define format_sysname_tlv format_generic_tlv #define format_sysobject_tlv format_generic_tlv #define format_mgmt_addr_tlv format_generic_tlv #define format_physical_loc_tlv format_generic_tlv #define format_mgmt_addr2_tlv format_generic_tlv #define format_power_requested_tlv format_generic_tlv #define format_power_available_tlv format_generic_tlv #define format_port_unidirectional_tlv format_generic_tlv #define format_unknown_28_tlv format_generic_tlv #define format_energywise_tlv format_generic_tlv #define format_unknown_30_tlv format_generic_tlv #define format_spare_poe_tlv format_generic_tlv /* tlv ID=0 is a mistake */ static cdp_error_t process_unused_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t) { return CDP_ERROR_BAD_TLV; } /* list of text TLV's that we snapshoot */ #define foreach_text_to_struct_tlv \ _(device_name,DEBUG_TLV_DUMP) \ _(version,DEBUG_TLV_DUMP) \ _(platform,DEBUG_TLV_DUMP) \ _(port_id,DEBUG_TLV_DUMP) #define _(z,dbg) \ static \ cdp_error_t process_##z##_tlv (cdp_main_t *cm, cdp_neighbor_t *n, \ cdp_tlv_t *t) \ { \ int i; \ if (dbg) \ fformat(stdout, "%U\n", format_text_tlv, t); \ \ if (n->z) \ _vec_len(n->z) = 0; \ \ for (i = 0; i < (t->l - sizeof (*t)); i++) \ vec_add1(n->z, t->v[i]); \ \ vec_add1(n->z, 0); \ \ return CDP_ERROR_NONE; \ } foreach_text_to_struct_tlv #undef _ #define process_address_tlv process_generic_tlv #define process_capabilities_tlv process_generic_tlv #define process_ipprefix_tlv process_generic_tlv #define process_hello_tlv process_generic_tlv #define process_vtp_domain_tlv process_generic_tlv #define process_native_vlan_tlv process_generic_tlv #define process_duplex_tlv process_generic_tlv #define process_appl_vlan_tlv process_generic_tlv #define process_trigger_tlv process_generic_tlv #define process_power_tlv process_generic_tlv #define process_mtu_tlv process_generic_tlv #define process_trust_tlv process_generic_tlv #define process_cos_tlv process_generic_tlv #define process_sysname_tlv process_generic_tlv #define process_sysobject_tlv process_generic_tlv #define process_mgmt_addr_tlv process_generic_tlv #define process_physical_loc_tlv process_generic_tlv #define process_mgmt_addr2_tlv process_generic_tlv #define process_power_requested_tlv process_generic_tlv #define process_power_available_tlv process_generic_tlv #define process_port_unidirectional_tlv process_generic_tlv #define process_unknown_28_tlv process_generic_tlv #define process_energywise_tlv process_generic_tlv #define process_unknown_30_tlv process_generic_tlv #define process_spare_poe_tlv process_generic_tlv static tlv_handler_t tlv_handlers[] = { #define _(a) {#a, CDP_TLV_##a, format_##a##_tlv, process_##a##_tlv}, foreach_cdp_tlv_type #undef _ }; #if DEBUG_TLV_DUMP == 0 CLIB_UNUSED (static u8 * format_cdp_hdr (u8 * s, va_list * va)); #endif static u8 * format_cdp_hdr (u8 * s, va_list * va) { cdp_hdr_t *h = va_arg (*va, cdp_hdr_t *); s = format (s, "version %d, ttl %d(secs), cksum 0x%04x\n", h->version, h->ttl, h->checksum); return s; } static cdp_error_t process_cdp_hdr (cdp_main_t * cm, cdp_neighbor_t * n, cdp_hdr_t * h) { #if DEBUG_TLV_DUMP > 0 fformat (stdout, "%U", format_cdp_hdr, h); #endif if (h->version != 1 && h->version != 2) return CDP_ERROR_PROTOCOL_VERSION; n->ttl_in_seconds = h->ttl; return CDP_ERROR_NONE; } /* scan a cdp packet; header, then tlv's */ static int cdp_packet_scan (cdp_main_t * cm, cdp_neighbor_t * n) { u8 *end, *cur = n->last_rx_pkt; cdp_hdr_t *h; cdp_tlv_t *tlv; cdp_error_t e = CDP_ERROR_NONE; tlv_handler_t *handler; cdp_error_t (*fp) (cdp_main_t *, cdp_neighbor_t *, cdp_tlv_t *); u16 computed_checksum; computed_checksum = cdp_checksum (cur, vec_len (cur)); if (computed_checksum) return CDP_ERROR_CHECKSUM; h = (cdp_hdr_t *) cur; e = process_cdp_hdr (cm, n, h); if (e) return e; // there are no tlvs if (vec_len (n->last_rx_pkt) <= 0) return CDP_ERROR_BAD_TLV; cur = (u8 *) (h + 1); end = n->last_rx_pkt + vec_len (n->last_rx_pkt) - 1; // look ahead 4 bytes (u16 tlv->t + u16 tlv->l) while (cur + 3 <= end) { tlv = (cdp_tlv_t *) cur; tlv->t = ntohs (tlv->t); tlv->l = ntohs (tlv->l); /* tlv length includes t, l and v */ if (tlv->l < 4) return CDP_ERROR_BAD_TLV; cur += tlv->l; if ((cur - 1) > end) return CDP_ERROR_BAD_TLV; /* * Only process know