LCOV - code coverage report
Current view: top level - plugins/cdp - cdp_periodic.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 17 193 8.8 %
Date: 2023-10-26 01:39:38 Functions: 3 16 18.8 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2011-2018 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             : #include <vppinfra/hash.h>
      17             : #include <vppinfra/pcap.h>
      18             : #include <vnet/srp/srp.h>
      19             : #include <vnet/ppp/ppp.h>
      20             : #include <vnet/hdlc/hdlc.h>
      21             : #include <vnet/srp/packet.h>
      22             : 
      23             : /*
      24             :  * Generate a set of specific CDP TLVs.
      25             :  *
      26             :  * $$$ eventually these need to fish better data from
      27             :  * other data structures; e.g. the hostname, software version info
      28             :  * etc.
      29             :  */
      30             : 
      31             : static void
      32           0 : add_device_name_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
      33             : {
      34           0 :   cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
      35             : 
      36           0 :   t->t = htons (CDP_TLV_device_name);
      37           0 :   t->l = htons (3 + sizeof (*t));
      38           0 :   clib_memcpy (&t->v, "VPP", 3);
      39             : 
      40           0 :   *t0p += ntohs (t->l);
      41           0 : }
      42             : 
      43             : static void
      44           0 : add_port_id_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
      45             : {
      46           0 :   cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
      47             : 
      48           0 :   t->t = htons (CDP_TLV_port_id);
      49           0 :   t->l = htons (vec_len (hw->name) + sizeof (*t));
      50           0 :   clib_memcpy (&t->v, hw->name, vec_len (hw->name));
      51           0 :   *t0p += ntohs (t->l);
      52           0 : }
      53             : 
      54             : static void
      55           0 : add_version_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
      56             : {
      57           0 :   cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
      58             : 
      59           0 :   t->t = htons (CDP_TLV_version);
      60           0 :   t->l = htons (12 + sizeof (*t));
      61           0 :   clib_memcpy (&t->v, "VPP Software", 12);
      62           0 :   *t0p += ntohs (t->l);
      63           0 : }
      64             : 
      65             : static void
      66           0 : add_platform_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
      67             : {
      68           0 :   cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
      69             : 
      70           0 :   t->t = htons (CDP_TLV_platform);
      71           0 :   t->l = htons (2 + sizeof (*t));
      72           0 :   clib_memcpy (&t->v, "SW", 2);
      73           0 :   *t0p += ntohs (t->l);
      74           0 : }
      75             : 
      76             : static void
      77           0 : add_capability_tlv (vnet_hw_interface_t * hw, u8 ** t0p)
      78             : {
      79           0 :   cdp_tlv_t *t = (cdp_tlv_t *) * t0p;
      80             :   u32 capabilities;
      81             : 
      82           0 :   t->t = htons (CDP_TLV_capabilities);
      83           0 :   t->l = htons (4 + sizeof (*t));
      84           0 :   capabilities = CDP_ROUTER_DEVICE;
      85           0 :   capabilities = htonl (capabilities);
      86           0 :   clib_memcpy (&t->v, &capabilities, sizeof (capabilities));
      87           0 :   *t0p += ntohs (t->l);
      88           0 : }
      89             : 
      90             : static void
      91           0 : add_tlvs (cdp_main_t * cm, vnet_hw_interface_t * hw, u8 ** t0p)
      92             : {
      93           0 :   add_device_name_tlv (hw, t0p);
      94           0 :   add_port_id_tlv (hw, t0p);
      95           0 :   add_version_tlv (hw, t0p);
      96           0 :   add_platform_tlv (hw, t0p);
      97           0 :   add_capability_tlv (hw, t0p);
      98           0 : }
      99             : 
     100             : /*
     101             :  * send a cdp pkt on an ethernet interface
     102             :  */
     103             : static void
     104           0 : send_ethernet_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
     105             : {
     106             :   u32 *to_next;
     107             :   ethernet_llc_snap_and_cdp_header_t *h0;
     108             :   vnet_hw_interface_t *hw;
     109             :   u32 bi0;
     110             :   vlib_buffer_t *b0;
     111             :   u8 *t0;
     112             :   u16 checksum;
     113             :   int nbytes_to_checksum;
     114             :   int i;
     115             :   vlib_frame_t *f;
     116           0 :   vlib_main_t *vm = cm->vlib_main;
     117           0 :   vnet_main_t *vnm = cm->vnet_main;
     118             : 
     119           0 :   for (i = 0; i < count; i++)
     120             :     {
     121             :       /*
     122             :        * see cdp_periodic_init() to understand what's already painted
     123             :        * into the buffer by the packet template mechanism
     124             :        */
     125           0 :       h0 = vlib_packet_template_get_packet
     126           0 :         (vm, &cm->packet_templates[n->packet_template_index], &bi0);
     127             : 
     128           0 :       if (!h0)
     129           0 :         break;
     130             : 
     131             :       /* Add the interface's ethernet source address */
     132           0 :       hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
     133             : 
     134           0 :       clib_memcpy (h0->ethernet.src_address, hw->hw_address,
     135             :                    vec_len (hw->hw_address));
     136             : 
     137           0 :       t0 = (u8 *) & h0->cdp.data;
     138             : 
     139             :       /* add TLVs */
     140           0 :       add_tlvs (cm, hw, &t0);
     141             : 
     142             :       /* add the cdp packet checksum */
     143           0 :       nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
     144           0 :       checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
     145           0 :       h0->cdp.checksum = htons (checksum);
     146             : 
     147             :       /* Set the outbound packet length */
     148           0 :       b0 = vlib_get_buffer (vm, bi0);
     149           0 :       b0->current_length = nbytes_to_checksum + sizeof (*h0)
     150           0 :         - sizeof (cdp_hdr_t);
     151             : 
     152             :       /* And the outbound interface */
     153           0 :       vnet_buffer (b0)->sw_if_index[VLIB_TX] = hw->sw_if_index;
     154             : 
     155             :       /* Set the 802.3 ethernet length */
     156           0 :       h0->ethernet.len = htons (b0->current_length
     157           0 :                                 - sizeof (ethernet_802_3_header_t));
     158             : 
     159             :       /* And output the packet on the correct interface */
     160           0 :       f = vlib_get_frame_to_node (vm, hw->output_node_index);
     161           0 :       to_next = vlib_frame_vector_args (f);
     162           0 :       to_next[0] = bi0;
     163           0 :       f->n_vectors = 1;
     164             : 
     165           0 :       vlib_put_frame_to_node (vm, hw->output_node_index, f);
     166           0 :       n->last_sent = vlib_time_now (vm);
     167             :     }
     168           0 : }
     169             : 
     170             : /*
     171             :  * send a cdp pkt on an hdlc interface
     172             :  */
     173             : static void
     174           0 : send_hdlc_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
     175             : {
     176             :   u32 *to_next;
     177             :   hdlc_and_cdp_header_t *h0;
     178             :   vnet_hw_interface_t *hw;
     179             :   u32 bi0;
     180             :   vlib_buffer_t *b0;
     181             :   u8 *t0;
     182             :   u16 checksum;
     183             :   int nbytes_to_checksum;
     184             :   int i;
     185             :   vlib_frame_t *f;
     186           0 :   vlib_main_t *vm = cm->vlib_main;
     187           0 :   vnet_main_t *vnm = cm->vnet_main;
     188             : 
     189           0 :   for (i = 0; i < count; i++)
     190             :     {
     191             :       /*
     192             :        * see cdp_periodic_init() to understand what's already painted
     193             :        * into the buffer by the packet template mechanism
     194             :        */
     195           0 :       h0 = vlib_packet_template_get_packet
     196           0 :         (vm, &cm->packet_templates[n->packet_template_index], &bi0);
     197             : 
     198           0 :       if (!h0)
     199           0 :         break;
     200             : 
     201           0 :       hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
     202             : 
     203           0 :       t0 = (u8 *) & h0->cdp.data;
     204             : 
     205             :       /* add TLVs */
     206           0 :       add_tlvs (cm, hw, &t0);
     207             : 
     208             :       /* add the cdp packet checksum */
     209           0 :       nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
     210           0 :       checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
     211           0 :       h0->cdp.checksum = htons (checksum);
     212             : 
     213             :       /* Set the outbound packet length */
     214           0 :       b0 = vlib_get_buffer (vm, bi0);
     215           0 :       b0->current_length = nbytes_to_checksum + sizeof (*h0)
     216           0 :         - sizeof (cdp_hdr_t);
     217             : 
     218             :       /* And output the packet on the correct interface */
     219           0 :       f = vlib_get_frame_to_node (vm, hw->output_node_index);
     220           0 :       to_next = vlib_frame_vector_args (f);
     221           0 :       to_next[0] = bi0;
     222           0 :       f->n_vectors = 1;
     223             : 
     224           0 :       vlib_put_frame_to_node (vm, hw->output_node_index, f);
     225           0 :       n->last_sent = vlib_time_now (vm);
     226             :     }
     227           0 : }
     228             : 
     229             : /*
     230             :  * send a cdp pkt on an srp interface
     231             :  */
     232             : static void
     233           0 : send_srp_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
     234             : {
     235             :   u32 *to_next;
     236             :   srp_and_cdp_header_t *h0;
     237             :   vnet_hw_interface_t *hw;
     238             :   u32 bi0;
     239             :   vlib_buffer_t *b0;
     240             :   u8 *t0;
     241             :   u16 checksum;
     242             :   int nbytes_to_checksum;
     243             :   int i;
     244             :   vlib_frame_t *f;
     245           0 :   vlib_main_t *vm = cm->vlib_main;
     246           0 :   vnet_main_t *vnm = cm->vnet_main;
     247             : 
     248           0 :   for (i = 0; i < count; i++)
     249             :     {
     250             :       /*
     251             :        * see cdp_periodic_init() to understand what's already painted
     252             :        * into the buffer by the packet template mechanism
     253             :        */
     254           0 :       h0 = vlib_packet_template_get_packet
     255           0 :         (vm, &cm->packet_templates[n->packet_template_index], &bi0);
     256             : 
     257           0 :       if (!h0)
     258           0 :         break;
     259             : 
     260           0 :       hw = vnet_get_sup_hw_interface (vnm, n->sw_if_index);
     261             : 
     262           0 :       t0 = (u8 *) & h0->cdp.data;
     263             : 
     264             :       /* add TLVs */
     265           0 :       add_tlvs (cm, hw, &t0);
     266             : 
     267             :       /* Add the interface's ethernet source address */
     268           0 :       clib_memcpy (h0->ethernet.src_address, hw->hw_address,
     269             :                    vec_len (hw->hw_address));
     270             : 
     271             :       /* add the cdp packet checksum */
     272           0 :       nbytes_to_checksum = t0 - (u8 *) & h0->cdp;
     273           0 :       checksum = cdp_checksum (&h0->cdp, nbytes_to_checksum);
     274           0 :       h0->cdp.checksum = htons (checksum);
     275             : 
     276             :       /* Set the outbound packet length */
     277           0 :       b0 = vlib_get_buffer (vm, bi0);
     278           0 :       b0->current_length = nbytes_to_checksum + sizeof (*h0)
     279           0 :         - sizeof (cdp_hdr_t);
     280             : 
     281             :       /* And output the packet on the correct interface */
     282           0 :       f = vlib_get_frame_to_node (vm, hw->output_node_index);
     283           0 :       to_next = vlib_frame_vector_args (f);
     284           0 :       to_next[0] = bi0;
     285           0 :       f->n_vectors = 1;
     286             : 
     287           0 :       vlib_put_frame_to_node (vm, hw->output_node_index, f);
     288           0 :       n->last_sent = vlib_time_now (vm);
     289             :     }
     290           0 : }
     291             : 
     292             : /*
     293             :  * Decide which cdp packet template to use
     294             :  */
     295             : static int
     296           0 : pick_packet_template (cdp_main_t * cm, cdp_neighbor_t * n)
     297             : {
     298           0 :   n->packet_template_index = CDP_PACKET_TEMPLATE_ETHERNET;
     299             : 
     300           0 :   return 0;
     301             : }
     302             : 
     303             : /* Send a cdp neighbor announcement */
     304             : static void
     305           0 : send_hello (cdp_main_t * cm, cdp_neighbor_t * n, int count)
     306             : {
     307           0 :   if (n->packet_template_index == (u8) ~ 0)
     308             :     {
     309             :       /* If we don't know how to talk to this peer, don't try again */
     310           0 :       if (pick_packet_template (cm, n))
     311             :         {
     312           0 :           n->last_sent = 1e70;
     313           0 :           return;
     314             :         }
     315             :     }
     316             : 
     317           0 :   switch (n->packet_template_index)
     318             :     {
     319           0 :     case CDP_PACKET_TEMPLATE_ETHERNET:
     320           0 :       send_ethernet_hello (cm, n, count);
     321           0 :       break;
     322             : 
     323           0 :     case CDP_PACKET_TEMPLATE_HDLC:
     324           0 :       send_hdlc_hello (cm, n, count);
     325           0 :       break;
     326             : 
     327           0 :     case CDP_PACKET_TEMPLATE_SRP:
     328           0 :       send_srp_hello (cm, n, count);
     329           0 :       break;
     330             : 
     331           0 :     default:
     332           0 :       ASSERT (0);
     333             :     }
     334           0 :   n->last_sent = vlib_time_now (cm->vlib_main);
     335             : }
     336             : 
     337             : static void
     338           0 : delete_neighbor (cdp_main_t * cm, cdp_neighbor_t * n, int want_broadcast)
     339             : {
     340           0 :   hash_unset (cm->neighbor_by_sw_if_index, n->sw_if_index);
     341           0 :   vec_free (n->device_name);
     342           0 :   vec_free (n->version);
     343           0 :   vec_free (n->port_id);
     344           0 :   vec_free (n->platform);
     345           0 :   vec_free (n->last_rx_pkt);
     346           0 :   pool_put (cm->neighbors, n);
     347           0 : }
     348             : 
     349             : void
     350           0 : cdp_periodic (vlib_main_t * vm)
     351             : {
     352           0 :   cdp_main_t *cm = &cdp_main;
     353             :   cdp_neighbor_t *n;
     354           0 :   f64 now = vlib_time_now (vm);
     355             :   vnet_sw_interface_t *sw;
     356             :   static u32 *delete_list = 0;
     357             :   int i;
     358             :   static cdp_neighbor_t **n_list = 0;
     359             : 
     360             :   /* *INDENT-OFF* */
     361           0 :   pool_foreach (n, cm->neighbors)
     362             :    {
     363           0 :     vec_add1 (n_list, n);
     364             :   }
     365             :   /* *INDENT-ON* */
     366             : 
     367             :   /* Across all cdp neighbors known to the system */
     368           0 :   for (i = 0; i < vec_len (n_list); i++)
     369             :     {
     370           0 :       n = n_list[i];
     371             : 
     372             :       /* "no cdp run" provisioned on the interface? */
     373           0 :       if (n->disabled == 1)
     374           0 :         continue;
     375             : 
     376           0 :       sw = vnet_get_sw_interface (cm->vnet_main, n->sw_if_index);
     377             : 
     378             :       /* Interface shutdown or rx timeout? */
     379           0 :       if (!(sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
     380           0 :           || (now > (n->last_heard + (f64) n->ttl_in_seconds)))
     381             :         /* add to list of neighbors to delete */
     382           0 :         vec_add1 (delete_list, n - cm->neighbors);
     383           0 :       else if (n->last_sent == 0.0)
     384             :         /* First time, send 3 hellos */
     385           0 :         send_hello (cm, n, 3 /* three to begin with */ );
     386           0 :       else if (now > (n->last_sent + (((f64) n->ttl_in_seconds) / 6.0)))
     387             :         /* Normal keepalive, send one */
     388           0 :         send_hello (cm, n, 1 /* one as a keepalive */ );
     389             :     }
     390             : 
     391           0 :   for (i = 0; i < vec_len (delete_list); i++)
     392             :     {
     393           0 :       n = vec_elt_at_index (cm->neighbors, delete_list[i]);
     394           0 :       delete_neighbor (cm, n, 1);
     395             :     }
     396           0 :   if (delete_list)
     397           0 :     vec_set_len (delete_list, 0);
     398           0 :   if (n_list)
     399           0 :     vec_set_len (n_list, 0);
     400           0 : }
     401             : 
     402             : static clib_error_t *
     403         575 : cdp_periodic_init (vlib_main_t * vm)
     404             : {
     405         575 :   cdp_main_t *cm = &cdp_main;
     406             : 
     407             :   /* Create the ethernet cdp hello packet template */
     408             :   {
     409             :     ethernet_llc_snap_and_cdp_header_t h;
     410             : 
     411         575 :     clib_memset (&h, 0, sizeof (h));
     412             : 
     413             :     /* Send to 01:00:0c:cc:cc */
     414         575 :     h.ethernet.dst_address[0] = 0x01;
     415             :     /* h.ethernet.dst_address[1] = 0x00; (clib_memset) */
     416         575 :     h.ethernet.dst_address[2] = 0x0C;
     417         575 :     h.ethernet.dst_address[3] = 0xCC;
     418         575 :     h.ethernet.dst_address[4] = 0xCC;
     419         575 :     h.ethernet.dst_address[5] = 0xCC;
     420             : 
     421             :     /* leave src address blank (fill in at send time) */
     422             : 
     423             :     /* leave length blank (fill in at send time) */
     424             : 
     425             :     /* LLC */
     426         575 :     h.llc.dst_sap = h.llc.src_sap = 0xAA;       /* SNAP */
     427         575 :     h.llc.control = 0x03;       /* UI (no extended control bytes) */
     428             : 
     429             :     /* SNAP */
     430             :     /* h.snap.oui[0] = 0x00; (clib_memset) */
     431             :     /* h.snap.oui[1] = 0x00; (clib_memset) */
     432         575 :     h.snap.oui[2] = 0x0C;       /* Cisco = 0x00000C */
     433         575 :     h.snap.protocol = htons (0x2000);   /* CDP = 0x2000 */
     434             : 
     435             :     /* CDP */
     436         575 :     h.cdp.version = 2;
     437         575 :     h.cdp.ttl = 180;
     438             : 
     439         575 :     vlib_packet_template_init
     440             :       (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_ETHERNET],
     441             :        /* data */ &h,
     442             :        sizeof (h),
     443             :        /* alloc chunk size */ 8,
     444             :        "cdp-ethernet");
     445             :   }
     446             : 
     447             : #if 0                           /* retain for reference */
     448             : 
     449             :   /* Create the hdlc cdp hello packet template */
     450             :   {
     451             :     hdlc_and_cdp_header_t h;
     452             : 
     453             :     clib_memset (&h, 0, sizeof (h));
     454             : 
     455             :     h.hdlc.address = 0x0f;
     456             :     /* h.hdlc.control = 0; (clib_memset) */
     457             :     h.hdlc.protocol = htons (0x2000);   /* CDP = 0x2000 */
     458             : 
     459             :     /* CDP */
     460             :     h.cdp.version = 2;
     461             :     h.cdp.ttl = 180;
     462             : 
     463             :     vlib_packet_template_init
     464             :       (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_HDLC],
     465             :        /* data */ &h,
     466             :        sizeof (h),
     467             :        /* alloc chunk size */ 8,
     468             :        "cdp-hdlc");
     469             :   }
     470             : 
     471             :   /* Create the srp cdp hello packet template */
     472             :   {
     473             :     srp_and_cdp_header_t h;
     474             : 
     475             :     clib_memset (&h, 0, sizeof (h));
     476             : 
     477             :     /* Send to 01:00:0c:cc:cc */
     478             :     h.ethernet.dst_address[0] = 0x01;
     479             :     /* h.ethernet.dst_address[1] = 0x00; (clib_memset) */
     480             :     h.ethernet.dst_address[2] = 0x0C;
     481             :     h.ethernet.dst_address[3] = 0xCC;
     482             :     h.ethernet.dst_address[4] = 0xCC;
     483             :     h.ethernet.dst_address[5] = 0xCC;
     484             : 
     485             :     /* leave src address blank (fill in at send time) */
     486             : 
     487             :     /* The srp header is filled in at xmt */
     488             :     h.srp.ttl = 1;
     489             :     h.srp.priority = 7;
     490             :     h.srp.mode = SRP_MODE_data;
     491             :     srp_header_compute_parity (&h.srp);
     492             : 
     493             :     /* Inner ring and parity will be set at send time */
     494             : 
     495             :     h.ethernet.type = htons (0x2000);   /* CDP = 0x2000 */
     496             : 
     497             :     /* CDP */
     498             :     h.cdp.version = 2;
     499             :     h.cdp.ttl = 180;
     500             : 
     501             :     vlib_packet_template_init
     502             :       (vm, &cm->packet_templates[CDP_PACKET_TEMPLATE_SRP],
     503             :        /* data */ &h,
     504             :        sizeof (h),
     505             :        /* alloc chunk size */ 8,
     506             :        "cdp-srp");
     507             :   }
     508             : #endif
     509             : 
     510         575 :   return 0;
     511             : }
     512             : 
     513        1151 : VLIB_INIT_FUNCTION (cdp_periodic_init);
     514             : 
     515             : /*
     516             :  * fd.io coding-style-patch-verification: ON
     517             :  *
     518             :  * Local Variables:
     519             :  * eval: (c-set-style "gnu")
     520             :  * End:
     521             :  */

Generated by: LCOV version 1.14