LCOV - code coverage report
Current view: top level - plugins/cdp - cdp_input.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 127 148 85.8 %
Date: 2023-07-05 22:20:52 Functions: 18 22 81.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2011-2016 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : #include <cdp/cdp.h>
      16             : 
      17             : cdp_main_t cdp_main;
      18             : 
      19             : #define DEBUG_TLV_DUMP 0        /* 1=> dump TLV's to stdout while processing them */
      20             : 
      21             : /*
      22             :  * ported from an unspecified Cisco cdp implementation.
      23             :  * Compute / return in HOST byte order. 0 => good checksum.
      24             :  */
      25             : u16
      26           3 : cdp_checksum (void *p, int count)
      27             : {
      28             :   u32 sum;
      29             :   u16 i, *data;
      30             : 
      31           3 :   data = p;
      32           3 :   sum = 0;
      33          39 :   while (count > 1)
      34             :     {
      35          36 :       sum += ntohs (*data);
      36          36 :       data++;
      37          36 :       count -= 2;
      38             :     }
      39             : 
      40           3 :   if (count > 0)
      41           3 :     sum += *(char *) data;
      42             : 
      43           4 :   while (sum >> 16)
      44             :     {
      45           1 :       sum = (sum & 0xFFFF) + (sum >> 16);
      46             :     }
      47             : 
      48           3 :   i = (i16) sum;
      49           3 :   return (~i);
      50             : }
      51             : 
      52             : /* TLV handler table */
      53             : typedef struct
      54             : {
      55             :   char *name;
      56             :   u32 tlv_id;
      57             :   void *format;
      58             :   void *process;
      59             : } tlv_handler_t;
      60             : 
      61             : static tlv_handler_t tlv_handlers[];
      62             : 
      63             : /* Display a generic TLV as a set of hex bytes */
      64             : static u8 *
      65           0 : format_generic_tlv (u8 * s, va_list * va)
      66             : {
      67           0 :   cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *);
      68           0 :   tlv_handler_t *h = &tlv_handlers[t->t];
      69             : 
      70           0 :   s = format (s, "%s(%d): %U\n", h->name,
      71           0 :               t->t, format_hex_bytes, t->v, t->l - sizeof (*t));
      72           0 :   return s;
      73             : }
      74             : 
      75             : /* Ignore / skip a TLV we don't support */
      76             : static cdp_error_t
      77           0 : process_generic_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
      78             : {
      79             : #if DEBUG_TLV_DUMP > 0
      80             :   fformat (stdout, "%U", format_generic_tlv, t);
      81             : #endif
      82             : 
      83           0 :   return CDP_ERROR_NONE;
      84             : }
      85             : 
      86             : /* print a text tlv */
      87             : static u8 *
      88           6 : format_text_tlv (u8 * s, va_list * va)
      89             : {
      90           6 :   cdp_tlv_t *t = va_arg (*va, cdp_tlv_t *);
      91           6 :   tlv_handler_t *h = &tlv_handlers[t->t];
      92             :   int i;
      93             : 
      94           6 :   s = format (s, "%s(%d): ", h->name, t->t);
      95             : 
      96           6 :   if (t->l >= 4)
      97             :     {
      98          46 :       for (i = 0; i < (t->l - sizeof (*t)); i++)
      99          41 :         vec_add1 (s, t->v[i]);
     100             :     }
     101             : 
     102           6 :   vec_add1 (s, '\n');
     103           6 :   return s;
     104             : }
     105             : 
     106             : #if DEBUG_TLV_DUMP == 0
     107             : /* gcc warning be gone */
     108             : CLIB_UNUSED (static cdp_error_t
     109             :              process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n,
     110             :                                cdp_tlv_t * t));
     111             : #endif
     112             : 
     113             : /* process / skip a generic text TLV that we don't support */
     114             : static cdp_error_t
     115           0 : process_text_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
     116             : {
     117             : #if DEBUG_TLV_DUMP > 0
     118             :   fformat (stdout, "%U\n", format_text_tlv, t);
     119             : #endif
     120             : 
     121           0 :   return CDP_ERROR_NONE;
     122             : }
     123             : 
     124             : /* per-TLV format function definitions */
     125             : #define format_unused_tlv format_generic_tlv
     126             : #define format_device_name_tlv format_text_tlv
     127             : #define format_address_tlv format_generic_tlv
     128             : #define format_port_id_tlv format_text_tlv
     129             : #define format_capabilities_tlv format_generic_tlv
     130             : #define format_version_tlv format_text_tlv
     131             : #define format_platform_tlv format_text_tlv
     132             : #define format_ipprefix_tlv format_generic_tlv
     133             : #define format_hello_tlv format_generic_tlv
     134             : #define format_vtp_domain_tlv format_generic_tlv
     135             : #define format_native_vlan_tlv format_generic_tlv
     136             : #define format_duplex_tlv format_generic_tlv
     137             : #define format_appl_vlan_tlv format_generic_tlv
     138             : #define format_trigger_tlv format_generic_tlv
     139             : #define format_power_tlv format_generic_tlv
     140             : #define format_mtu_tlv format_generic_tlv
     141             : #define format_trust_tlv format_generic_tlv
     142             : #define format_cos_tlv format_generic_tlv
     143             : #define format_sysname_tlv format_generic_tlv
     144             : #define format_sysobject_tlv format_generic_tlv
     145             : #define format_mgmt_addr_tlv format_generic_tlv
     146             : #define format_physical_loc_tlv format_generic_tlv
     147             : #define format_mgmt_addr2_tlv format_generic_tlv
     148             : #define format_power_requested_tlv format_generic_tlv
     149             : #define format_power_available_tlv format_generic_tlv
     150             : #define format_port_unidirectional_tlv format_generic_tlv
     151             : #define format_unknown_28_tlv format_generic_tlv
     152             : #define format_energywise_tlv format_generic_tlv
     153             : #define format_unknown_30_tlv format_generic_tlv
     154             : #define format_spare_poe_tlv format_generic_tlv
     155             : 
     156             : /* tlv ID=0 is a mistake */
     157             : static cdp_error_t
     158           0 : process_unused_tlv (cdp_main_t * cm, cdp_neighbor_t * n, cdp_tlv_t * t)
     159             : {
     160           0 :   return CDP_ERROR_BAD_TLV;
     161             : }
     162             : 
     163             : /* list of text TLV's that we snapshoot */
     164             : #define foreach_text_to_struct_tlv              \
     165             : _(device_name,DEBUG_TLV_DUMP)                   \
     166             : _(version,DEBUG_TLV_DUMP)                       \
     167             : _(platform,DEBUG_TLV_DUMP)                      \
     168             : _(port_id,DEBUG_TLV_DUMP)
     169             : 
     170             : #define _(z, dbg)                                                             \
     171             :   static cdp_error_t process_##z##_tlv (cdp_main_t *cm, cdp_neighbor_t *n,    \
     172             :                                         cdp_tlv_t *t)                         \
     173             :   {                                                                           \
     174             :     int i;                                                                    \
     175             :     if (dbg)                                                                  \
     176             :       fformat (stdout, "%U\n", format_text_tlv, t);                           \
     177             :                                                                               \
     178             :     if (n->z)                                                                 \
     179             :       vec_set_len (n->z, 0);                                                  \
     180             :                                                                               \
     181             :     for (i = 0; i < (t->l - sizeof (*t)); i++)                                \
     182             :       vec_add1 (n->z, t->v[i]);                                               \
     183             :                                                                               \
     184             :     vec_add1 (n->z, 0);                                                       \
     185             :                                                                               \
     186             :     return CDP_ERROR_NONE;                                                    \
     187             :   }
     188             : 
     189          41 : foreach_text_to_struct_tlv
     190             : #undef _
     191             : #define process_address_tlv process_generic_tlv
     192             : #define process_capabilities_tlv process_generic_tlv
     193             : #define process_ipprefix_tlv process_generic_tlv
     194             : #define process_hello_tlv process_generic_tlv
     195             : #define process_vtp_domain_tlv process_generic_tlv
     196             : #define process_native_vlan_tlv process_generic_tlv
     197             : #define process_duplex_tlv process_generic_tlv
     198             : #define process_appl_vlan_tlv process_generic_tlv
     199             : #define process_trigger_tlv process_generic_tlv
     200             : #define process_power_tlv process_generic_tlv
     201             : #define process_mtu_tlv process_generic_tlv
     202             : #define process_trust_tlv process_generic_tlv
     203             : #define process_cos_tlv process_generic_tlv
     204             : #define process_sysname_tlv process_generic_tlv
     205             : #define process_sysobject_tlv process_generic_tlv
     206             : #define process_mgmt_addr_tlv process_generic_tlv
     207             : #define process_physical_loc_tlv process_generic_tlv
     208             : #define process_mgmt_addr2_tlv process_generic_tlv
     209             : #define process_power_requested_tlv process_generic_tlv
     210             : #define process_power_available_tlv process_generic_tlv
     211             : #define process_port_unidirectional_tlv process_generic_tlv
     212             : #define process_unknown_28_tlv process_generic_tlv
     213             : #define process_energywise_tlv process_generic_tlv
     214             : #define process_unknown_30_tlv process_generic_tlv
     215             : #define process_spare_poe_tlv process_generic_tlv
     216             : static tlv_handler_t tlv_handlers[] = {
     217             : #define _(a) {#a, CDP_TLV_##a, format_##a##_tlv, process_##a##_tlv},
     218             :   foreach_cdp_tlv_type
     219             : #undef _
     220             : };
     221             : 
     222             : #if DEBUG_TLV_DUMP == 0
     223             : CLIB_UNUSED (static u8 * format_cdp_hdr (u8 * s, va_list * va));
     224             : #endif
     225             : 
     226             : static u8 *
     227           3 : format_cdp_hdr (u8 * s, va_list * va)
     228             : {
     229           3 :   cdp_hdr_t *h = va_arg (*va, cdp_hdr_t *);
     230             : 
     231           3 :   s = format (s, "version %d, ttl %d(secs), cksum 0x%04x\n",
     232           3 :               h->version, h->ttl, h->checksum);
     233           3 :   return s;
     234             : }
     235             : 
     236             : static cdp_error_t
     237           3 : process_cdp_hdr (cdp_main_t * cm, cdp_neighbor_t * n, cdp_hdr_t * h)
     238             : {
     239             : #if DEBUG_TLV_DUMP > 0
     240             :   fformat (stdout, "%U", format_cdp_hdr, h);
     241             : #endif
     242             : 
     243           3 :   if (h->version != 1 && h->version != 2)
     244           0 :     return CDP_ERROR_PROTOCOL_VERSION;
     245             : 
     246           3 :   n->ttl_in_seconds = h->ttl;
     247             : 
     248           3 :   return CDP_ERROR_NONE;
     249             : }
     250             : 
     251             : /* scan a cdp packet; header, then tlv's */
     252             : static int
     253           3 : cdp_packet_scan (cdp_main_t * cm, cdp_neighbor_t * n)
     254             : {
     255           3 :   u8 *end, *cur = n->last_rx_pkt;
     256             :   cdp_hdr_t *h;
     257             :   cdp_tlv_t *tlv;
     258           3 :   cdp_error_t e = CDP_ERROR_NONE;
     259             :   tlv_handler_t *handler;
     260             :   cdp_error_t (*fp) (cdp_main_t *, cdp_neighbor_t *, cdp_tlv_t *);
     261             :   u16 computed_checksum;
     262             : 
     263           3 :   computed_checksum = cdp_checksum (cur, vec_len (cur));
     264             : 
     265           3 :   if (computed_checksum)
     266           0 :     return CDP_ERROR_CHECKSUM;
     267             : 
     268           3 :   h = (cdp_hdr_t *) cur;
     269             : 
     270           3 :   e = process_cdp_hdr (cm, n, h);
     271           3 :   if (e)
     272           0 :     return e;
     273             : 
     274             :   // there are no tlvs
     275           3 :   if (vec_len (n->last_rx_pkt) <= 0)
     276           0 :     return CDP_ERROR_BAD_TLV;
     277             : 
     278           3 :   cur = (u8 *) (h + 1);
     279           3 :   end = n->last_rx_pkt + vec_len (n->last_rx_pkt) - 1;
     280             : 
     281             :   // look ahead 4 bytes (u16 tlv->t + u16 tlv->l)
     282           7 :   while (cur + 3 <= end)
     283             :     {
     284           6 :       tlv = (cdp_tlv_t *) cur;
     285           6 :       tlv->t = ntohs (tlv->t);
     286           6 :       tlv->l = ntohs (tlv->l);
     287             : 
     288             :       /* tlv length includes t, l and v */
     289             : 
     290           6 :       if (tlv->l < 4)
     291           1 :         return CDP_ERROR_BAD_TLV;
     292             : 
     293           5 :       cur += tlv->l;
     294           5 :       if ((cur - 1) > end)
     295           1 :         return CDP_ERROR_BAD_TLV;
     296             : 
     297             :       /*
     298             :        * Only process known TLVs. In practice, certain
     299             :        * devices send tlv->t = 0xFF, perhaps as an EOF of sorts.
     300             :        */
     301           4 :       if (tlv->t < ARRAY_LEN (tlv_handlers))
     302             :         {
     303           4 :           handler = &tlv_handlers[tlv->t];
     304           4 :           fp = handler->process;
     305           4 :           e = (*fp) (cm, n, tlv);
     306           4 :           if (e)
     307           0 :             return e;
     308             :         }
     309             :     }
     310             : 
     311             :   // did not process all tlvs or none tlv processed
     312           1 :   if ((cur - 1) != end)
     313           0 :     return CDP_ERROR_BAD_TLV;
     314             : 
     315           1 :   return CDP_ERROR_NONE;
     316             : }
     317             : 
     318             : /*
     319             :  * cdp input routine
     320             :  */
     321             : cdp_error_t
     322           3 : cdp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
     323             : {
     324           3 :   cdp_main_t *cm = &cdp_main;
     325             :   cdp_neighbor_t *n;
     326             :   uword *p, nbytes;
     327             :   cdp_error_t e;
     328             :   uword last_packet_signature;
     329             : 
     330             :   /* find or create a neighbor pool entry for the (sw) interface
     331             :      upon which we received this pkt */
     332           3 :   p = hash_get (cm->neighbor_by_sw_if_index,
     333             :                 vnet_buffer (b0)->sw_if_index[VLIB_RX]);
     334             : 
     335           3 :   if (p == 0)
     336             :     {
     337           1 :       pool_get (cm->neighbors, n);
     338           1 :       clib_memset (n, 0, sizeof (*n));
     339           1 :       n->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
     340           1 :       n->packet_template_index = (u8) ~ 0;
     341           1 :       hash_set (cm->neighbor_by_sw_if_index, n->sw_if_index,
     342             :                 n - cm->neighbors);
     343             :     }
     344             :   else
     345             :     {
     346           2 :       n = pool_elt_at_index (cm->neighbors, p[0]);
     347             :     }
     348             : 
     349             :   /*
     350             :    * typical clib idiom. Don't repeatedly allocate and free
     351             :    * the per-neighbor rx buffer. Reset its apparent length to zero
     352             :    * and reuse it.
     353             :    */
     354             : 
     355           3 :   if (n->last_rx_pkt)
     356           2 :     vec_set_len (n->last_rx_pkt, 0);
     357             : 
     358             :   /* cdp disabled on this interface, we're done */
     359           3 :   if (n->disabled)
     360           0 :     return CDP_ERROR_DISABLED;
     361             : 
     362             :   /*
     363             :    * Make sure the per-neighbor rx buffer is big enough to hold
     364             :    * the data we're about to copy
     365             :    */
     366           3 :   vec_validate (n->last_rx_pkt, vlib_buffer_length_in_chain (vm, b0) - 1);
     367             : 
     368             :   /*
     369             :    * Coalesce / copy e the buffer chain into the per-neighbor
     370             :    * rx buffer
     371             :    */
     372           3 :   nbytes = vlib_buffer_contents (vm, bi0, n->last_rx_pkt);
     373           3 :   ASSERT (nbytes <= vec_len (n->last_rx_pkt));
     374             : 
     375             :   /*
     376             :    * Compute Jenkins hash of the new packet, decide if we need to
     377             :    * actually parse through the TLV's. CDP packets are all identical,
     378             :    * so unless we time out the peer, we don't need to process the packet.
     379             :    */
     380             :   last_packet_signature =
     381           3 :     hash_memory (n->last_rx_pkt, vec_len (n->last_rx_pkt), 0xd00b);
     382             : 
     383           3 :   if (n->last_packet_signature_valid &&
     384           2 :       n->last_packet_signature == last_packet_signature)
     385             :     {
     386           0 :       e = CDP_ERROR_CACHE_HIT;
     387             :     }
     388             :   else
     389             :     {
     390             :       /* Actually scan the packet */
     391           3 :       e = cdp_packet_scan (cm, n);
     392           3 :       n->last_packet_signature_valid = 1;
     393           3 :       n->last_packet_signature = last_packet_signature;
     394             :     }
     395             : 
     396           3 :   if (e == CDP_ERROR_NONE)
     397             :     {
     398           1 :       n->last_heard = vlib_time_now (vm);
     399             :     }
     400             : 
     401           3 :   return e;
     402             : }
     403             : 
     404             : /*
     405             :  * setup neighbor hash table
     406             :  */
     407             : static clib_error_t *
     408         559 : cdp_input_init (vlib_main_t * vm)
     409             : {
     410         559 :   cdp_main_t *cm = &cdp_main;
     411             : 
     412         559 :   cm->vlib_main = vm;
     413         559 :   cm->vnet_main = vnet_get_main ();
     414         559 :   cm->neighbor_by_sw_if_index = hash_create (0, sizeof (uword));
     415             : 
     416         559 :   return 0;
     417             : }
     418             : 
     419             : /* *INDENT-OFF* */
     420        1119 : VLIB_INIT_FUNCTION (cdp_input_init) =
     421             : {
     422             :   .runs_after = VLIB_INITS("cdp_periodic_init"),
     423             : };
     424             : /* *INDENT-ON* */
     425             : 
     426             : 
     427             : static u8 *
     428           2 : format_cdp_neighbors (u8 * s, va_list * va)
     429             : {
     430           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
     431           2 :   cdp_main_t *cm = va_arg (*va, cdp_main_t *);
     432           2 :   vnet_main_t *vnm = &vnet_main;
     433             :   cdp_neighbor_t *n;
     434             :   vnet_hw_interface_t *hw;
     435             : 
     436           2 :   s = format (s,
     437             :               "%=25s %=25s %=25s %=10s\n",
     438             :               "Our Port", "Peer System", "Peer Port", "Last Heard");
     439             : 
     440             :   /* *INDENT-OFF* */
     441           4 :   pool_foreach (n, cm->neighbors)
     442             :    {
     443           2 :     hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
     444             : 
     445           2 :     if (n->disabled == 0)
     446           2 :       s = format (s, "%=25v %=25s %=25s %=10.1f\n",
     447             :                   hw->name, n->device_name, n->port_id,
     448             :                   n->last_heard);
     449             :   }
     450             :   /* *INDENT-ON* */
     451           2 :   return s;
     452             : }
     453             : 
     454             : static clib_error_t *
     455           2 : show_cdp (vlib_main_t * vm,
     456             :           unformat_input_t * input, vlib_cli_command_t * cmd)
     457             : {
     458           2 :   cdp_main_t *cm = &cdp_main;
     459             : 
     460           2 :   if (cm->enabled == 0)
     461           0 :     vlib_cli_output (vm, "CDP is not enabled...");
     462             :   else
     463           2 :     vlib_cli_output (vm, "%U\n", format_cdp_neighbors, vm, cm);
     464             : 
     465           2 :   return 0;
     466             : }
     467             : 
     468             : /* *INDENT-OFF* */
     469      252167 : VLIB_CLI_COMMAND (show_cdp_command, static) = {
     470             :   .path = "show cdp",
     471             :   .short_help = "Show cdp command",
     472             :   .function = show_cdp,
     473             : };
     474             : /* *INDENT-ON* */
     475             : 
     476             : 
     477             : /*
     478             :  * packet trace format function, very similar to
     479             :  * cdp_packet_scan except that we call the per TLV format
     480             :  * functions instead of the per TLV processing functions
     481             :  */
     482             : u8 *
     483           3 : cdp_input_format_trace (u8 * s, va_list * args)
     484             : {
     485           3 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     486           3 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     487           3 :   cdp_input_trace_t *t = va_arg (*args, cdp_input_trace_t *);
     488             :   u8 *cur;
     489             :   cdp_hdr_t *h;
     490             :   cdp_tlv_t *tlv;
     491             :   tlv_handler_t *handler;
     492             :   u8 *(*fp) (cdp_tlv_t *);
     493             : 
     494           3 :   cur = t->data;
     495             : 
     496           3 :   h = (cdp_hdr_t *) cur;
     497           3 :   s = format (s, "%U", format_cdp_hdr, h);
     498             : 
     499           3 :   cur = (u8 *) (h + 1);
     500             : 
     501           9 :   while (cur < t->data + t->len)
     502             :     {
     503           7 :       tlv = (cdp_tlv_t *) cur;
     504           7 :       tlv->t = ntohs (tlv->t);
     505           7 :       tlv->l = ntohs (tlv->l);
     506           7 :       if (tlv->t >= ARRAY_LEN (tlv_handlers))
     507             :         {
     508           1 :           s = format (s, "BAD_TLV\n");
     509           1 :           break;
     510             :         }
     511           6 :       handler = &tlv_handlers[tlv->t];
     512           6 :       fp = handler->format;
     513           6 :       s = format (s, "  %U", fp, tlv);
     514             :       /* tlv length includes (t, l) */
     515           6 :       cur += tlv->l;
     516             :     }
     517             : 
     518           3 :   return s;
     519             : }
     520             : 
     521             : /*
     522             :  * fd.io coding-style-patch-verification: ON
     523             :  *
     524             :  * Local Variables:
     525             :  * eval: (c-set-style "gnu")
     526             :  * End:
     527             :  */

Generated by: LCOV version 1.14