LCOV - code coverage report
Current view: top level - plugins/lldp - lldp_input.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 13 126 10.3 %
Date: 2023-10-26 01:39:38 Functions: 4 13 30.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             : /**
      16             :  * @file
      17             :  * @brief LLDP packet parsing implementation
      18             :  */
      19             : #include <lldp/lldp_node.h>
      20             : #include <lldp/lldp_protocol.h>
      21             : #include <vlibmemory/api.h>
      22             : 
      23             : typedef struct
      24             : {
      25             :   u32 hw_if_index;
      26             :   u8 chassis_id_len;
      27             :   u8 chassis_id_subtype;
      28             :   u8 portid_len;
      29             :   u8 portid_subtype;
      30             :   u16 ttl;
      31             :   u8 data[0];                   /* this contains both chassis id (chassis_id_len bytes) and port
      32             :                                    id (portid_len bytes) */
      33             : } lldp_intf_update_t;
      34             : 
      35             : static void
      36           0 : lldp_rpc_update_peer_cb (const lldp_intf_update_t * a)
      37             : {
      38           0 :   ASSERT (vlib_get_thread_index () == 0);
      39             : 
      40           0 :   lldp_intf_t *n = lldp_get_intf (&lldp_main, a->hw_if_index);
      41           0 :   if (!n)
      42             :     {
      43             :       /* LLDP turned off for this interface, ignore the update */
      44           0 :       return;
      45             :     }
      46           0 :   const u8 *chassis_id = a->data;
      47           0 :   const u8 *portid = a->data + a->chassis_id_len;
      48             : 
      49           0 :   if (n->chassis_id)
      50             :     {
      51           0 :       vec_set_len (n->chassis_id, 0);
      52             :     }
      53           0 :   vec_add (n->chassis_id, chassis_id, a->chassis_id_len);
      54           0 :   n->chassis_id_subtype = a->chassis_id_subtype;
      55           0 :   if (n->port_id)
      56             :     {
      57           0 :       vec_set_len (n->port_id, 0);
      58             :     }
      59           0 :   vec_add (n->port_id, portid, a->portid_len);
      60           0 :   n->port_id_subtype = a->portid_subtype;
      61           0 :   n->ttl = a->ttl;
      62           0 :   n->last_heard = vlib_time_now (lldp_main.vlib_main);
      63             : }
      64             : 
      65             : static void
      66           0 : lldp_rpc_update_peer (u32 hw_if_index, const u8 * chid, u8 chid_len,
      67             :                       u8 chid_subtype, const u8 * portid,
      68             :                       u8 portid_len, u8 portid_subtype, u16 ttl)
      69           0 : {
      70           0 :   const size_t data_size =
      71           0 :     sizeof (lldp_intf_update_t) + chid_len + portid_len;
      72           0 :   u8 data[data_size];
      73           0 :   lldp_intf_update_t *u = (lldp_intf_update_t *) data;
      74           0 :   u->hw_if_index = hw_if_index;
      75           0 :   u->chassis_id_len = chid_len;
      76           0 :   u->chassis_id_subtype = chid_subtype;
      77           0 :   u->ttl = ttl;
      78           0 :   u->portid_len = portid_len;
      79           0 :   u->portid_subtype = portid_subtype;
      80           0 :   clib_memcpy (u->data, chid, chid_len);
      81           0 :   clib_memcpy (u->data + chid_len, portid, portid_len);
      82           0 :   vl_api_rpc_call_main_thread (lldp_rpc_update_peer_cb, data, data_size);
      83           0 : }
      84             : 
      85             : lldp_tlv_code_t
      86           0 : lldp_tlv_get_code (const lldp_tlv_t * tlv)
      87             : {
      88           0 :   return tlv->head.byte1 >> 1;
      89             : }
      90             : 
      91             : void
      92           0 : lldp_tlv_set_code (lldp_tlv_t * tlv, lldp_tlv_code_t code)
      93             : {
      94           0 :   tlv->head.byte1 = (tlv->head.byte1 & 1) + (code << 1);
      95           0 : }
      96             : 
      97             : u16
      98           0 : lldp_tlv_get_length (const lldp_tlv_t * tlv)
      99             : {
     100           0 :   return (((u16) (tlv->head.byte1 & 1)) << 8) + tlv->head.byte2;
     101             : }
     102             : 
     103             : void
     104           0 : lldp_tlv_set_length (lldp_tlv_t * tlv, u16 length)
     105             : {
     106           0 :   tlv->head.byte2 = length & ((1 << 8) - 1);
     107           0 :   if (length > (1 << 8) - 1)
     108             :     {
     109           0 :       tlv->head.byte1 |= 1;
     110             :     }
     111             :   else
     112             :     {
     113           0 :       tlv->head.byte1 &= (1 << 8) - 2;
     114             :     }
     115           0 : }
     116             : 
     117             : lldp_main_t lldp_main;
     118             : 
     119             : static int
     120           0 : lldp_packet_scan (u32 hw_if_index, const lldp_tlv_t * pkt)
     121             : {
     122           0 :   const lldp_tlv_t *tlv = pkt;
     123             : 
     124             : #define TLV_VIOLATES_PKT_BOUNDARY(pkt, tlv)                               \
     125             :   (((((u8 *)tlv) + sizeof (lldp_tlv_t)) > ((u8 *)pkt + vec_len (pkt))) || \
     126             :    ((((u8 *)tlv) + lldp_tlv_get_length (tlv)) > ((u8 *)pkt + vec_len (pkt))))
     127             : 
     128             :   /* first tlv is always chassis id, followed by port id and ttl tlvs */
     129           0 :   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
     130           0 :       LLDP_TLV_NAME (chassis_id) != lldp_tlv_get_code (tlv))
     131             :     {
     132           0 :       return LLDP_ERROR_BAD_TLV;
     133             :     }
     134             : 
     135           0 :   u16 l = lldp_tlv_get_length (tlv);
     136           0 :   if (l < STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
     137           0 :       LLDP_MIN_CHASS_ID_LEN ||
     138             :       l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
     139             :       LLDP_MAX_CHASS_ID_LEN)
     140             :     {
     141           0 :       return LLDP_ERROR_BAD_TLV;
     142             :     }
     143             : 
     144           0 :   u8 chid_subtype = ((lldp_chassis_id_tlv_t *) tlv)->subtype;
     145           0 :   u8 *chid = ((lldp_chassis_id_tlv_t *) tlv)->id;
     146           0 :   u8 chid_len = l - STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype);
     147             : 
     148           0 :   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
     149             : 
     150           0 :   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
     151           0 :       LLDP_TLV_NAME (port_id) != lldp_tlv_get_code (tlv))
     152             :     {
     153           0 :       return LLDP_ERROR_BAD_TLV;
     154             :     }
     155           0 :   l = lldp_tlv_get_length (tlv);
     156           0 :   if (l < STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype) +
     157           0 :       LLDP_MIN_PORT_ID_LEN ||
     158             :       l > STRUCT_SIZE_OF (lldp_chassis_id_tlv_t, subtype) +
     159             :       LLDP_MAX_PORT_ID_LEN)
     160             :     {
     161           0 :       return LLDP_ERROR_BAD_TLV;
     162             :     }
     163             : 
     164           0 :   u8 portid_subtype = ((lldp_port_id_tlv_t *) tlv)->subtype;
     165           0 :   u8 *portid = ((lldp_port_id_tlv_t *) tlv)->id;
     166           0 :   u8 portid_len = l - STRUCT_SIZE_OF (lldp_port_id_tlv_t, subtype);
     167             : 
     168           0 :   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
     169             : 
     170           0 :   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
     171           0 :       LLDP_TLV_NAME (ttl) != lldp_tlv_get_code (tlv))
     172             :     {
     173           0 :       return LLDP_ERROR_BAD_TLV;
     174             :     }
     175           0 :   l = lldp_tlv_get_length (tlv);
     176           0 :   if (l != STRUCT_SIZE_OF (lldp_ttl_tlv_t, ttl))
     177             :     {
     178           0 :       return LLDP_ERROR_BAD_TLV;
     179             :     }
     180           0 :   u16 ttl = ntohs (((lldp_ttl_tlv_t *) tlv)->ttl);
     181           0 :   tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) + l);
     182           0 :   while (!TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) &&
     183           0 :          LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv))
     184             :     {
     185           0 :       switch (lldp_tlv_get_code (tlv))
     186             :         {
     187             : #define F(num, type, str)     \
     188             :   case LLDP_TLV_NAME (type):  \
     189             :     /* ignore optional TLV */ \
     190             :     break;
     191           0 :           foreach_lldp_optional_tlv_type (F);
     192             : #undef F
     193           0 :         default:
     194           0 :           return LLDP_ERROR_BAD_TLV;
     195             :         }
     196           0 :       tlv = (lldp_tlv_t *) ((u8 *) tlv + STRUCT_SIZE_OF (lldp_tlv_t, head) +
     197           0 :                             lldp_tlv_get_length (tlv));
     198             :     }
     199             :   /* last tlv is pdu_end */
     200           0 :   if (TLV_VIOLATES_PKT_BOUNDARY (pkt, tlv) ||
     201           0 :       LLDP_TLV_NAME (pdu_end) != lldp_tlv_get_code (tlv) ||
     202           0 :       0 != lldp_tlv_get_length (tlv))
     203             :     {
     204           0 :       return LLDP_ERROR_BAD_TLV;
     205             :     }
     206           0 :   lldp_rpc_update_peer (hw_if_index, chid, chid_len, chid_subtype, portid,
     207             :                         portid_len, portid_subtype, ttl);
     208           0 :   return LLDP_ERROR_NONE;
     209             : }
     210             : 
     211             : lldp_intf_t *
     212       26850 : lldp_get_intf (lldp_main_t * lm, u32 hw_if_index)
     213             : {
     214       26850 :   uword *p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
     215             : 
     216       26850 :   if (p)
     217             :     {
     218           0 :       return pool_elt_at_index (lm->intfs, p[0]);
     219             :     }
     220       26850 :   return NULL;
     221             : }
     222             : 
     223             : lldp_intf_t *
     224           0 : lldp_create_intf (lldp_main_t * lm, u32 hw_if_index)
     225             : {
     226             : 
     227             :   uword *p;
     228             :   lldp_intf_t *n;
     229           0 :   p = hash_get (lm->intf_by_hw_if_index, hw_if_index);
     230             : 
     231           0 :   if (p == 0)
     232             :     {
     233           0 :       pool_get (lm->intfs, n);
     234           0 :       clib_memset (n, 0, sizeof (*n));
     235           0 :       n->hw_if_index = hw_if_index;
     236           0 :       hash_set (lm->intf_by_hw_if_index, n->hw_if_index, n - lm->intfs);
     237             :     }
     238             :   else
     239             :     {
     240           0 :       n = pool_elt_at_index (lm->intfs, p[0]);
     241             :     }
     242           0 :   return n;
     243             : }
     244             : 
     245             : /*
     246             :  * lldp input routine
     247             :  */
     248             : lldp_error_t
     249           0 : lldp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0)
     250             : {
     251           0 :   lldp_main_t *lm = &lldp_main;
     252             :   lldp_error_t e;
     253             : 
     254             :   /* find our interface */
     255           0 :   vnet_sw_interface_t *sw_interface = vnet_get_sw_interface (lm->vnet_main,
     256           0 :                                                              vnet_buffer
     257             :                                                              (b0)->sw_if_index
     258             :                                                              [VLIB_RX]);
     259           0 :   lldp_intf_t *n = lldp_get_intf (lm, sw_interface->hw_if_index);
     260             : 
     261           0 :   if (!n)
     262             :     {
     263             :       /* lldp disabled on this interface, we're done */
     264           0 :       return LLDP_ERROR_DISABLED;
     265             :     }
     266             : 
     267             :   /* Actually scan the packet */
     268           0 :   e = lldp_packet_scan (sw_interface->hw_if_index,
     269           0 :                         vlib_buffer_get_current (b0));
     270             : 
     271           0 :   return e;
     272             : }
     273             : 
     274             : /*
     275             :  * setup function
     276             :  */
     277             : static clib_error_t *
     278         575 : lldp_init (vlib_main_t * vm)
     279             : {
     280             :   clib_error_t *error;
     281         575 :   lldp_main_t *lm = &lldp_main;
     282             : 
     283         575 :   if ((error = vlib_call_init_function (vm, lldp_template_init)))
     284           0 :     return error;
     285             : 
     286         575 :   lm->vlib_main = vm;
     287         575 :   lm->vnet_main = vnet_get_main ();
     288         575 :   lm->msg_tx_hold = 4;               /* default value per IEEE 802.1AB-2009 */
     289         575 :   lm->msg_tx_interval = 30;  /* default value per IEEE 802.1AB-2009 */
     290             : 
     291         575 :   return 0;
     292             : }
     293             : 
     294        1151 : VLIB_INIT_FUNCTION (lldp_init);
     295             : 
     296             : /*
     297             :  * fd.io coding-style-patch-verification: ON
     298             :  *
     299             :  * Local Variables:
     300             :  * eval: (c-set-style "gnu")
     301             :  * End:
     302             :  */

Generated by: LCOV version 1.14