LCOV - code coverage report
Current view: top level - vnet/ip6-nd - ip6_nd.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 111 121 91.7 %
Date: 2023-10-26 01:39:38 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /*
       2             :  * ip/ip6_neighbor.c: IP6 neighbor handling
       3             :  *
       4             :  * Copyright (c) 2010 Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : #include <vnet/ip6-nd/ip6_nd.h>
      19             : #include <vnet/ip6-nd/ip6_nd_inline.h>
      20             : 
      21             : #include <vnet/ip-neighbor/ip_neighbor.h>
      22             : #include <vnet/ip-neighbor/ip_neighbor_dp.h>
      23             : 
      24             : #include <vnet/fib/ip6_fib.h>
      25             : #include <vnet/ip/ip6_link.h>
      26             : #include <vnet/ip/ip6_ll_table.h>
      27             : 
      28             : /**
      29             :  * @file
      30             :  * @brief IPv6 Neighbor Adjacency and Neighbor Discovery.
      31             :  *
      32             :  * The files contains the API and CLI code for managing IPv6 neighbor
      33             :  * adjacency tables and neighbor discovery logic.
      34             :  */
      35             : 
      36             : #define DEF_MAX_RADV_INTERVAL 200
      37             : #define DEF_MIN_RADV_INTERVAL .75 * DEF_MAX_RADV_INTERVAL
      38             : 
      39             : typedef struct ip6_nd_t_
      40             : {
      41             :   /* local information */
      42             :   u32 sw_if_index;
      43             : 
      44             :   /* stats */
      45             :   u32 n_solicitations_rcvd;
      46             :   u32 n_solicitations_dropped;
      47             : } ip6_nd_t;
      48             : 
      49             : static ip6_link_delegate_id_t ip6_nd_delegate_id;
      50             : static ip6_nd_t *ip6_nd_pool;
      51             : 
      52             : static_always_inline uword
      53        1901 : icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
      54             :                                               vlib_node_runtime_t * node,
      55             :                                               vlib_frame_t * frame,
      56             :                                               uword is_solicitation)
      57             : {
      58        1901 :   ip6_main_t *im = &ip6_main;
      59        1901 :   uword n_packets = frame->n_vectors;
      60             :   u32 *from, *to_next;
      61             :   u32 n_left_from, n_left_to_next, next_index, n_advertisements_sent;
      62             :   icmp6_neighbor_discovery_option_type_t option_type;
      63             :   vlib_node_runtime_t *error_node =
      64        1901 :     vlib_node_get_runtime (vm, ip6_icmp_input_node.index);
      65             : 
      66        1901 :   from = vlib_frame_vector_args (frame);
      67        1901 :   n_left_from = n_packets;
      68        1901 :   next_index = node->cached_next_index;
      69             : 
      70        1901 :   if (node->flags & VLIB_NODE_FLAG_TRACE)
      71        1656 :     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
      72             :                                    /* stride */ 1,
      73             :                                    sizeof (icmp6_input_trace_t));
      74             : 
      75        1901 :   option_type =
      76             :     (is_solicitation
      77             :      ? ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address
      78        1901 :      : ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address);
      79        1901 :   n_advertisements_sent = 0;
      80             : 
      81        3802 :   while (n_left_from > 0)
      82             :     {
      83        1901 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
      84             : 
      85        3806 :       while (n_left_from > 0 && n_left_to_next > 0)
      86             :         {
      87             :           vlib_buffer_t *p0;
      88             :           ip6_header_t *ip0;
      89             :           icmp6_neighbor_solicitation_or_advertisement_header_t *h0;
      90             :           icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *o0;
      91             :           u32 bi0, options_len0, sw_if_index0, next0, error0;
      92             :           u32 ip6_sadd_link_local, ip6_sadd_unspecified;
      93             :           ip_neighbor_counter_type_t c_type;
      94             :           int is_rewrite0;
      95             :           u32 ni0;
      96             : 
      97        1905 :           bi0 = to_next[0] = from[0];
      98             : 
      99        1905 :           from += 1;
     100        1905 :           to_next += 1;
     101        1905 :           n_left_from -= 1;
     102        1905 :           n_left_to_next -= 1;
     103             : 
     104        1905 :           p0 = vlib_get_buffer (vm, bi0);
     105        1905 :           ip0 = vlib_buffer_get_current (p0);
     106        1905 :           h0 = ip6_next_header (ip0);
     107        1905 :           options_len0 =
     108        1905 :             clib_net_to_host_u16 (ip0->payload_length) - sizeof (h0[0]);
     109             : 
     110        1905 :           error0 = ICMP6_ERROR_NONE;
     111        1905 :           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
     112        1905 :           ip6_sadd_link_local =
     113        1905 :             ip6_address_is_link_local_unicast (&ip0->src_address);
     114        1905 :           ip6_sadd_unspecified =
     115        1905 :             ip6_address_is_unspecified (&ip0->src_address);
     116             : 
     117             :           /* Check that source address is unspecified, link-local or else on-link. */
     118        1905 :           if (!ip6_sadd_unspecified && !ip6_sadd_link_local)
     119             :             {
     120        1754 :               u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
     121             : 
     122        1754 :               if (ADJ_INDEX_INVALID != src_adj_index0)
     123             :                 {
     124        1752 :                   ip_adjacency_t *adj0 = adj_get (src_adj_index0);
     125             : 
     126             :                   /* Allow all realistic-looking rewrite adjacencies to pass */
     127        1752 :                   ni0 = adj0->lookup_next_index;
     128        1752 :                   is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
     129             :                     (ni0 < IP6_LOOKUP_N_NEXT);
     130             : 
     131        1752 :                   error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
     132        1749 :                              || !is_rewrite0)
     133             :                             ?
     134             :                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
     135        3501 :                             : error0);
     136             :                 }
     137             :               else
     138             :                 {
     139           2 :                   error0 =
     140             :                     ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK;
     141             :                 }
     142             :             }
     143             : 
     144        1905 :           o0 = (void *) (h0 + 1);
     145        1905 :           o0 = ((options_len0 == 8 && o0->header.type == option_type
     146        3810 :                  && o0->header.n_data_u64s == 1) ? o0 : 0);
     147             : 
     148             :           /* If src address unspecified or link local, donot learn neighbor MAC */
     149        1905 :           if (PREDICT_TRUE (error0 == ICMP6_ERROR_NONE && o0 != 0 &&
     150             :                             !ip6_sadd_unspecified))
     151             :             {
     152             :               /* *INDENT-OFF* */
     153        1752 :               ip_neighbor_learn_t learn = {
     154             :                 .sw_if_index = sw_if_index0,
     155             :                 .ip = {
     156             :                   .version = AF_IP6,
     157             :                   .ip.ip6 = (is_solicitation ?
     158             :                              ip0->src_address :
     159             :                              h0->target_address),
     160             :                 }
     161             :               };
     162             :               /* *INDENT-ON* */
     163        1752 :               memcpy (&learn.mac, o0->ethernet_address, sizeof (learn.mac));
     164        1752 :               ip_neighbor_learn_dp (&learn);
     165             :             }
     166             : 
     167        1905 :           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
     168             :             {
     169             :               /* Check that target address is local to this router. */
     170             :               fib_node_index_t fei;
     171             :               u32 fib_index;
     172             : 
     173             :               fib_index =
     174        1868 :                 ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
     175             : 
     176        1868 :               if (~0 == fib_index)
     177             :                 {
     178           0 :                   error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
     179             :                 }
     180             :               else
     181             :                 {
     182        1868 :                   if (ip6_address_is_link_local_unicast (&h0->target_address))
     183             :                     {
     184          74 :                       fei = ip6_fib_table_lookup_exact_match
     185             :                         (ip6_ll_fib_get (sw_if_index0),
     186          74 :                          &h0->target_address, 128);
     187             :                     }
     188             :                   else
     189             :                     {
     190        1794 :                       fei = ip6_fib_table_lookup_exact_match (fib_index,
     191        1794 :                                                               &h0->target_address,
     192             :                                                               128);
     193             :                     }
     194             : 
     195        1868 :                   if (FIB_NODE_INDEX_INVALID == fei)
     196             :                     {
     197             :                       /* The target address is not in the FIB */
     198         150 :                       error0 =
     199             :                         ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
     200             :                     }
     201             :                   else
     202             :                     {
     203        1718 :                       if (FIB_ENTRY_FLAG_LOCAL &
     204        1718 :                           fib_entry_get_flags_for_source (fei,
     205             :                                                           FIB_SOURCE_INTERFACE))
     206             :                         {
     207             :                           /* It's an address that belongs to one of our interfaces
     208             :                            * that's good. */
     209             :                         }
     210           4 :                       else if (FIB_ENTRY_FLAG_LOCAL &
     211           4 :                                fib_entry_get_flags_for_source (
     212             :                                  fei, FIB_SOURCE_IP6_ND))
     213             :                         {
     214             :                           /* It's one of our link local addresses
     215             :                            * that's good. */
     216             :                         }
     217           2 :                       else if (fib_entry_is_sourced (fei,
     218             :                                                      FIB_SOURCE_IP6_ND_PROXY))
     219             :                         {
     220             :                           /* The address was added by IPv6 Proxy ND config.
     221             :                            * We should only respond to these if the NS arrived on
     222             :                            * the link that has a matching covering prefix */
     223             :                         }
     224             :                       else
     225             :                         {
     226           1 :                           error0 =
     227             :                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
     228             :                         }
     229             :                     }
     230             :                 }
     231             :             }
     232             : 
     233        1905 :           if (is_solicitation)
     234             :             {
     235        1873 :               next0 = (error0 != ICMP6_ERROR_NONE ?
     236        1873 :                                ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP :
     237             :                                ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY);
     238        1873 :               c_type = IP_NEIGHBOR_CTR_REQUEST;
     239             :             }
     240             :           else
     241             :             {
     242          32 :               next0 = 0;
     243          32 :               error0 = error0 == ICMP6_ERROR_NONE ?
     244          32 :                 ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_RX : error0;
     245          32 :               c_type = IP_NEIGHBOR_CTR_REPLY;
     246             :             }
     247             : 
     248        1905 :           vlib_increment_simple_counter (
     249             :             &ip_neighbor_counters[AF_IP6].ipnc[VLIB_RX][c_type],
     250             :             vm->thread_index, sw_if_index0, 1);
     251             : 
     252        1905 :           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
     253             :             {
     254        1717 :               icmp6_send_neighbor_advertisement (vm, p0, ip0, h0, o0,
     255             :                                                  sw_if_index0);
     256        1717 :               n_advertisements_sent++;
     257             :             }
     258             : 
     259        1905 :           p0->error = error_node->errors[error0];
     260             : 
     261        1905 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
     262             :                                            to_next, n_left_to_next,
     263             :                                            bi0, next0);
     264             :         }
     265             : 
     266        1901 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     267             :     }
     268             : 
     269             :   /* Account for advertisements sent. */
     270        1901 :   vlib_error_count (vm, error_node->node_index,
     271             :                     ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX,
     272             :                     n_advertisements_sent);
     273             : 
     274        1901 :   return frame->n_vectors;
     275             : }
     276             : 
     277             : static const ethernet_interface_t *
     278        2113 : ip6_nd_get_eth_itf (u32 sw_if_index)
     279             : {
     280             :   const vnet_sw_interface_t *sw;
     281             : 
     282             :   /* lookup radv container  - ethernet interfaces only */
     283        2113 :   sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
     284        2113 :   if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
     285        2113 :     return (ethernet_get_interface (&ethernet_main, sw->hw_if_index));
     286             : 
     287           0 :   return (NULL);
     288             : }
     289             : 
     290             : /**
     291             :  * @brief called when IP6 is enabled on a link.
     292             :  * create and initialize router advertisement parameters with default
     293             :  * values for this intfc
     294             :  */
     295             : static void
     296        2113 : ip6_nd_link_enable (u32 sw_if_index)
     297             : {
     298             :   const ethernet_interface_t *eth;
     299             :   ip6_nd_t *ind;
     300             : 
     301        2113 :   eth = ip6_nd_get_eth_itf (sw_if_index);
     302             : 
     303        2113 :   if (NULL == eth)
     304         155 :     return;
     305             : 
     306        1958 :   ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
     307             :                                                   ip6_nd_delegate_id));
     308             : 
     309        1958 :   pool_get_zero (ip6_nd_pool, ind);
     310             : 
     311        1958 :   ind->sw_if_index = sw_if_index;
     312             : 
     313        1958 :   ip6_link_delegate_update (sw_if_index, ip6_nd_delegate_id,
     314        1958 :                             ind - ip6_nd_pool);
     315             : }
     316             : 
     317             : static void
     318        1787 : ip6_nd_delegate_disable (index_t indi)
     319             : {
     320             :   ip6_nd_t *ind;
     321             : 
     322        1787 :   ind = pool_elt_at_index (ip6_nd_pool, indi);
     323             : 
     324        1787 :   pool_put (ip6_nd_pool, ind);
     325        1787 : }
     326             : 
     327             : static uword
     328        1869 : icmp6_neighbor_solicitation (vlib_main_t * vm,
     329             :                              vlib_node_runtime_t * node, vlib_frame_t * frame)
     330             : {
     331        1869 :   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
     332             :                                                        /* is_solicitation */
     333             :                                                        1);
     334             : }
     335             : 
     336             : static uword
     337          32 : icmp6_neighbor_advertisement (vlib_main_t * vm,
     338             :                               vlib_node_runtime_t * node,
     339             :                               vlib_frame_t * frame)
     340             : {
     341          32 :   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
     342             :                                                        /* is_solicitation */
     343             :                                                        0);
     344             : }
     345             : 
     346             : /* *INDENT-OFF* */
     347      183788 : VLIB_REGISTER_NODE (ip6_icmp_neighbor_solicitation_node,static) =
     348             : {
     349             :   .function = icmp6_neighbor_solicitation,
     350             :   .name = "icmp6-neighbor-solicitation",
     351             : 
     352             :   .vector_size = sizeof (u32),
     353             : 
     354             :   .format_trace = format_icmp6_input_trace,
     355             : 
     356             :   .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
     357             :   .next_nodes = {
     358             :     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
     359             :     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
     360             :   },
     361             : };
     362             : 
     363      183788 : VLIB_REGISTER_NODE (ip6_icmp_neighbor_advertisement_node,static) =
     364             : {
     365             :   .function = icmp6_neighbor_advertisement,
     366             :   .name = "icmp6-neighbor-advertisement",
     367             : 
     368             :   .vector_size = sizeof (u32),
     369             : 
     370             :   .format_trace = format_icmp6_input_trace,
     371             : 
     372             :   .n_next_nodes = 1,
     373             :   .next_nodes = {
     374             :     [0] = "ip6-punt",
     375             :   },
     376             : };
     377             : /* *INDENT-ON* */
     378             : 
     379             : static u8 *
     380           0 : format_ip6_nd (u8 * s, va_list * args)
     381             : {
     382           0 :   CLIB_UNUSED (index_t indi) = va_arg (*args, index_t);
     383           0 :   u32 indent = va_arg (*args, u32);
     384             : 
     385           0 :   s = format (s, "%UNeighbor Discovery: enabled\n",
     386             :               format_white_space, indent);
     387             : 
     388           0 :   s = format (s, "%UICMP redirects are disabled\n",
     389             :               format_white_space, indent + 2);
     390           0 :   s = format (s, "%UICMP unreachables are not sent\n",
     391             :               format_white_space, indent + 2);
     392           0 :   s = format (s, "%UND DAD is disabled\n", format_white_space, indent + 2);
     393             :   //s = format (s, "%UND reachable time is %d milliseconds\n",);
     394             : 
     395           0 :   return (s);
     396             : }
     397             : 
     398             : /**
     399             :  * VFT to act as an implementation of a neighbour protocol
     400             :  */
     401             : const static ip_neighbor_vft_t ip6_nd_impl_vft = {
     402             :   .inv_proxy6_add = ip6_nd_proxy_add,
     403             :   .inv_proxy6_del = ip6_nd_proxy_del,
     404             : };
     405             : 
     406             : /**
     407             :  * VFT for registering as a delegate to an IP6 link
     408             :  */
     409             : const static ip6_link_delegate_vft_t ip6_nd_delegate_vft = {
     410             :   .ildv_disable = ip6_nd_delegate_disable,
     411             :   .ildv_enable = ip6_nd_link_enable,
     412             :   .ildv_format = format_ip6_nd,
     413             : };
     414             : 
     415             : static clib_error_t *
     416         575 : ip6_nd_init (vlib_main_t * vm)
     417             : {
     418         575 :   icmp6_register_type (vm, ICMP6_neighbor_solicitation,
     419             :                        ip6_icmp_neighbor_solicitation_node.index);
     420         575 :   icmp6_register_type (vm, ICMP6_neighbor_advertisement,
     421             :                        ip6_icmp_neighbor_advertisement_node.index);
     422             : 
     423         575 :   ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
     424             : 
     425         575 :   ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
     426             : 
     427         575 :   return 0;
     428             : }
     429             : 
     430             : /* *INDENT-OFF* */
     431       97343 : VLIB_INIT_FUNCTION (ip6_nd_init) =
     432             : {
     433             :   .runs_after = VLIB_INITS("icmp6_init"),
     434             : };
     435             : /* *INDENT-ON* */
     436             : 
     437             : /*
     438             :  * fd.io coding-style-patch-verification: ON
     439             :  *
     440             :  * Local Variables:
     441             :  * eval: (c-set-style "gnu")
     442             :  * End:
     443             :  */

Generated by: LCOV version 1.14