LCOV - code coverage report
Current view: top level - vnet/ip6-nd - ip6_mld.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 143 160 89.4 %
Date: 2023-10-26 01:39:38 Functions: 17 18 94.4 %

          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             : 
      20             : #include <vnet/ip/ip.h>
      21             : #include <vnet/ip-neighbor/ip_neighbor_dp.h>
      22             : 
      23             : #include <vnet/ip/ip6_link.h>
      24             : #include <vnet/ip/ip6_ll_table.h>
      25             : 
      26             : #include <vnet/ethernet/ethernet.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             : /* *INDENT-OFF*/
      37             : /* multicast listener report packet format for ethernet. */
      38             : typedef CLIB_PACKED (struct
      39             : {
      40             :     ip6_hop_by_hop_ext_t ext_hdr;
      41             :     ip6_router_alert_option_t alert;
      42             :     ip6_padN_option_t pad;
      43             :     icmp46_header_t icmp;
      44             :     u16 rsvd;
      45             :     u16 num_addr_records;
      46             :     icmp6_multicast_address_record_t records[0];
      47             : }) icmp6_multicast_listener_report_header_t;
      48             : 
      49             : typedef CLIB_PACKED (struct
      50             : {
      51             :   ip6_header_t ip;
      52             :   icmp6_multicast_listener_report_header_t report_hdr;
      53             : }) icmp6_multicast_listener_report_packet_t;
      54             : /* *INDENT-ON*/
      55             : 
      56             : typedef struct
      57             : {
      58             :   /* group information */
      59             :   u16 num_sources;
      60             :   u8 type;
      61             :   ip6_address_t mcast_address;
      62             :   ip6_address_t *mcast_source_address_pool;
      63             : } ip6_mldp_group_t;
      64             : 
      65             : typedef struct ip6_nd_t_
      66             : {
      67             :   /* local information */
      68             :   u32 sw_if_index;
      69             :   int all_routers_mcast;
      70             : 
      71             :   /* MLDP  group information */
      72             :   ip6_mldp_group_t *mldp_group_pool;
      73             : 
      74             :   /* Hash table mapping address to index in mldp address pool. */
      75             :   mhash_t address_to_mldp_index;
      76             : 
      77             : } ip6_mld_t;
      78             : 
      79             : 
      80             : static ip6_link_delegate_id_t ip6_mld_delegate_id;
      81             : static ip6_mld_t *ip6_mld_pool;
      82             : 
      83             : /////
      84             : 
      85             : static inline ip6_mld_t *
      86        1793 : ip6_mld_get_itf (u32 sw_if_index)
      87             : {
      88             :   index_t imi;
      89             : 
      90        1793 :   imi = ip6_link_delegate_get (sw_if_index, ip6_mld_delegate_id);
      91             : 
      92        1793 :   if (INDEX_INVALID != imi)
      93        1793 :     return (pool_elt_at_index (ip6_mld_pool, imi));
      94             : 
      95           0 :   return (NULL);
      96             : }
      97             : 
      98             : /**
      99             :  * @brief Add a multicast Address to the advertised MLD set
     100             :  */
     101             : static void
     102        7862 : ip6_neighbor_add_mld_prefix (ip6_mld_t * imd, ip6_address_t * addr)
     103             : {
     104             :   ip6_mldp_group_t *mcast_group_info;
     105             :   uword *p;
     106             : 
     107             :   /* lookup  mldp info for this interface */
     108        7862 :   p = mhash_get (&imd->address_to_mldp_index, addr);
     109        7862 :   mcast_group_info = p ? pool_elt_at_index (imd->mldp_group_pool, p[0]) : 0;
     110             : 
     111             :   /* add address */
     112        7862 :   if (!mcast_group_info)
     113             :     {
     114             :       /* add */
     115             :       u32 mi;
     116        7847 :       pool_get_zero (imd->mldp_group_pool, mcast_group_info);
     117             : 
     118        7847 :       mi = mcast_group_info - imd->mldp_group_pool;
     119        7847 :       mhash_set (&imd->address_to_mldp_index, addr, mi,  /* old_value */
     120             :                  0);
     121             : 
     122        7847 :       mcast_group_info->type = 4;
     123        7847 :       mcast_group_info->mcast_source_address_pool = 0;
     124        7847 :       mcast_group_info->num_sources = 0;
     125        7847 :       clib_memcpy (&mcast_group_info->mcast_address, addr,
     126             :                    sizeof (ip6_address_t));
     127             :     }
     128        7862 : }
     129             : 
     130             : /**
     131             :  * @brief Delete a multicast Address from the advertised MLD set
     132             :  */
     133             : static void
     134        1822 : ip6_neighbor_del_mld_prefix (ip6_mld_t * imd, ip6_address_t * addr)
     135             : {
     136             :   ip6_mldp_group_t *mcast_group_info;
     137             :   uword *p;
     138             : 
     139        1822 :   p = mhash_get (&imd->address_to_mldp_index, addr);
     140        1822 :   mcast_group_info = p ? pool_elt_at_index (imd->mldp_group_pool, p[0]) : 0;
     141             : 
     142        1822 :   if (mcast_group_info)
     143             :     {
     144        1807 :       mhash_unset (&imd->address_to_mldp_index, addr,
     145             :                    /* old_value */ 0);
     146        1807 :       pool_put (imd->mldp_group_pool, mcast_group_info);
     147             :     }
     148        1822 : }
     149             : 
     150             : /**
     151             :  * @brief Add a multicast Address to the advertised MLD set
     152             :  */
     153             : static void
     154        5874 : ip6_neighbor_add_mld_grp (ip6_mld_t * a,
     155             :                           ip6_multicast_address_scope_t scope,
     156             :                           ip6_multicast_link_local_group_id_t group)
     157             : {
     158             :   ip6_address_t addr;
     159             : 
     160        5874 :   ip6_set_reserved_multicast_address (&addr, scope, group);
     161             : 
     162        5874 :   ip6_neighbor_add_mld_prefix (a, &addr);
     163        5874 : }
     164             : 
     165             : static const ethernet_interface_t *
     166        2113 : ip6_mld_get_eth_itf (u32 sw_if_index)
     167             : {
     168             :   const vnet_sw_interface_t *sw;
     169             : 
     170             :   /* lookup radv container  - ethernet interfaces only */
     171        2113 :   sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
     172        2113 :   if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
     173        2113 :     return (ethernet_get_interface (&ethernet_main, sw->hw_if_index));
     174             : 
     175           0 :   return (NULL);
     176             : }
     177             : 
     178             : /**
     179             :  * @brief create and initialize router advertisement parameters with default
     180             :  * values for this intfc
     181             :  */
     182             : static void
     183        2113 : ip6_mld_link_enable (u32 sw_if_index)
     184             : {
     185             :   const ethernet_interface_t *eth;
     186             :   ip6_mld_t *imd;
     187             : 
     188        2113 :   eth = ip6_mld_get_eth_itf (sw_if_index);
     189             : 
     190        2113 :   if (NULL == eth)
     191         155 :     return;
     192             : 
     193        1958 :   ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
     194             :                                                   ip6_mld_delegate_id));
     195             : 
     196        1958 :   pool_get_zero (ip6_mld_pool, imd);
     197             : 
     198        1958 :   imd->sw_if_index = sw_if_index;
     199             : 
     200        1958 :   mhash_init (&imd->address_to_mldp_index, sizeof (uword),
     201             :               sizeof (ip6_address_t));
     202             : 
     203             :   /* add multicast groups we will always be reporting  */
     204        1958 :   ip6_neighbor_add_mld_grp (imd,
     205             :                             IP6_MULTICAST_SCOPE_link_local,
     206             :                             IP6_MULTICAST_GROUP_ID_all_hosts);
     207        1958 :   ip6_neighbor_add_mld_grp (imd,
     208             :                             IP6_MULTICAST_SCOPE_link_local,
     209             :                             IP6_MULTICAST_GROUP_ID_all_routers);
     210        1958 :   ip6_neighbor_add_mld_grp (imd,
     211             :                             IP6_MULTICAST_SCOPE_link_local,
     212             :                             IP6_MULTICAST_GROUP_ID_mldv2_routers);
     213             : 
     214        1958 :   ip6_link_delegate_update (sw_if_index, ip6_mld_delegate_id,
     215        1958 :                             imd - ip6_mld_pool);
     216             : }
     217             : 
     218             : static void
     219        1787 : ip6_mld_delegate_disable (index_t imdi)
     220             : {
     221             :   ip6_mldp_group_t *m;
     222             :   ip6_mld_t *imd;
     223             : 
     224        1787 :   imd = pool_elt_at_index (ip6_mld_pool, imdi);
     225             : 
     226             :   /* clean MLD pools */
     227             :   /* *INDENT-OFF* */
     228       12509 :   pool_flush (m, imd->mldp_group_pool,
     229             :   ({
     230             :     mhash_unset (&imd->address_to_mldp_index, &m->mcast_address, 0);
     231             :   }));
     232             :   /* *INDENT-ON* */
     233             : 
     234        1787 :   pool_free (imd->mldp_group_pool);
     235             : 
     236        1787 :   mhash_free (&imd->address_to_mldp_index);
     237             : 
     238        1787 :   pool_put (ip6_mld_pool, imd);
     239        1787 : }
     240             : 
     241             : /* send an mldpv2 report  */
     242             : static void
     243        1793 : ip6_neighbor_send_mldpv2_report (u32 sw_if_index)
     244             : {
     245        1793 :   vnet_main_t *vnm = vnet_get_main ();
     246        1793 :   vlib_main_t *vm = vnm->vlib_main;
     247             :   int bogus_length;
     248             : 
     249             :   ip6_mld_t *imd;
     250             :   u16 payload_length;
     251             :   vlib_buffer_t *b0;
     252             :   ip6_header_t *ip0;
     253             :   u32 *to_next;
     254             :   vlib_frame_t *f;
     255             :   u32 bo0;
     256        1793 :   u32 n_to_alloc = 1;
     257             : 
     258             :   icmp6_multicast_listener_report_header_t *rh0;
     259             :   icmp6_multicast_listener_report_packet_t *rp0;
     260             : 
     261        1793 :   if (!vnet_sw_interface_is_admin_up (vnm, sw_if_index))
     262           0 :     return;
     263             : 
     264        1793 :   imd = ip6_mld_get_itf (sw_if_index);
     265             : 
     266        1793 :   if (NULL == imd)
     267           0 :     return;
     268             : 
     269             :   /* send report now - build a mldpv2 report packet  */
     270        1793 :   if (0 == vlib_buffer_alloc (vm, &bo0, n_to_alloc))
     271             :     {
     272           0 :     alloc_fail:
     273           0 :       clib_warning ("buffer allocation failure");
     274           0 :       return;
     275             :     }
     276             : 
     277        1793 :   b0 = vlib_get_buffer (vm, bo0);
     278             : 
     279             :   /* adjust the sizeof the buffer to just include the ipv6 header */
     280        1793 :   b0->current_length = sizeof (icmp6_multicast_listener_report_packet_t);
     281             : 
     282        1793 :   payload_length = sizeof (icmp6_multicast_listener_report_header_t);
     283             : 
     284        1793 :   b0->error = ICMP6_ERROR_NONE;
     285             : 
     286        1793 :   rp0 = vlib_buffer_get_current (b0);
     287        1793 :   ip0 = (ip6_header_t *) & rp0->ip;
     288        1793 :   rh0 = (icmp6_multicast_listener_report_header_t *) & rp0->report_hdr;
     289             : 
     290        1793 :   clib_memset (rp0, 0x0, sizeof (icmp6_multicast_listener_report_packet_t));
     291             : 
     292        1793 :   ip0->ip_version_traffic_class_and_flow_label =
     293        1793 :     clib_host_to_net_u32 (0x6 << 28);
     294             : 
     295        1793 :   ip0->protocol = IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS;
     296             :   /* for DEBUG - vnet driver won't seem to emit router alerts */
     297             :   /* ip0->protocol = IP_PROTOCOL_ICMP6; */
     298        1793 :   ip0->hop_limit = 1;
     299             : 
     300        1793 :   rh0->icmp.type = ICMP6_multicast_listener_report_v2;
     301             : 
     302             :   /* source address MUST be the link-local address */
     303        1793 :   ip6_address_copy (&ip0->src_address,
     304             :                     ip6_get_link_local_address (sw_if_index));
     305             : 
     306             :   /* destination is all mldpv2 routers */
     307        1793 :   ip6_set_reserved_multicast_address (&ip0->dst_address,
     308             :                                       IP6_MULTICAST_SCOPE_link_local,
     309             :                                       IP6_MULTICAST_GROUP_ID_mldv2_routers);
     310             : 
     311             :   /* add reports here */
     312             :   ip6_mldp_group_t *m;
     313        1793 :   int num_addr_records = 0;
     314             :   icmp6_multicast_address_record_t rr;
     315             : 
     316             :   /* fill in the hop-by-hop extension header (router alert) info */
     317        1793 :   rh0->ext_hdr.next_hdr = IP_PROTOCOL_ICMP6;
     318        1793 :   rh0->ext_hdr.n_data_u64s = 0;
     319             : 
     320        1793 :   rh0->alert.type = IP6_MLDP_ALERT_TYPE;
     321        1793 :   rh0->alert.len = 2;
     322        1793 :   rh0->alert.value = 0;
     323             : 
     324        1793 :   rh0->pad.type = 1;
     325        1793 :   rh0->pad.len = 0;
     326             : 
     327        1793 :   rh0->icmp.checksum = 0;
     328             : 
     329             :   /* *INDENT-OFF* */
     330        8951 :   pool_foreach (m, imd->mldp_group_pool)
     331             :    {
     332        7158 :     rr.type = m->type;
     333        7158 :     rr.aux_data_len_u32s = 0;
     334        7158 :     rr.num_sources = clib_host_to_net_u16 (m->num_sources);
     335        7158 :     clib_memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
     336             : 
     337        7158 :     num_addr_records++;
     338             : 
     339        7158 :     if(vlib_buffer_add_data (vm, &bo0, (void *)&rr,
     340             :                              sizeof(icmp6_multicast_address_record_t)))
     341             :       {
     342           0 :         vlib_buffer_free (vm, &bo0, 1);
     343           0 :         goto alloc_fail;
     344             :       }
     345             : 
     346        7158 :     payload_length += sizeof( icmp6_multicast_address_record_t);
     347             :   }
     348             :   /* *INDENT-ON* */
     349             : 
     350        1793 :   rh0->rsvd = 0;
     351        1793 :   rh0->num_addr_records = clib_host_to_net_u16 (num_addr_records);
     352             : 
     353             :   /* update lengths */
     354        1793 :   ip0->payload_length = clib_host_to_net_u16 (payload_length);
     355             : 
     356        1793 :   rh0->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0,
     357             :                                                           &bogus_length);
     358        1793 :   ASSERT (bogus_length == 0);
     359             : 
     360             :   /*
     361             :    * OK to override w/ no regard for actual FIB, because
     362             :    * ip6-rewrite only looks at the adjacency.
     363             :    */
     364        1793 :   vnet_buffer (b0)->sw_if_index[VLIB_RX] =
     365        1793 :     vnet_main.local_interface_sw_if_index;
     366             : 
     367        3586 :   vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
     368        1793 :     ip6_link_get_mcast_adj (sw_if_index);
     369        1793 :   b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
     370             : 
     371        1793 :   vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite-mcast");
     372             : 
     373        1793 :   f = vlib_get_frame_to_node (vm, node->index);
     374        1793 :   to_next = vlib_frame_vector_args (f);
     375        1793 :   to_next[0] = bo0;
     376        1793 :   f->n_vectors = 1;
     377             : 
     378        1793 :   vlib_put_frame_to_node (vm, node->index, f);
     379        1793 :   return;
     380             : }
     381             : 
     382             : /* send a RA or update the timer info etc.. */
     383             : static uword
     384        7327 : ip6_mld_timer_event (vlib_main_t * vm,
     385             :                      vlib_node_runtime_t * node, vlib_frame_t * frame)
     386             : {
     387        7327 :   vnet_main_t *vnm = vnet_get_main ();
     388             :   ip6_mld_t *imd;
     389             : 
     390             :   /* Interface ip6 radv info list */
     391             :   /* *INDENT-OFF* */
     392       19650 :   pool_foreach (imd, ip6_mld_pool)
     393             :    {
     394       12323 :     if (!vnet_sw_interface_is_admin_up (vnm, imd->sw_if_index))
     395             :       {
     396          48 :         imd->all_routers_mcast = 0;
     397          48 :         continue;
     398             :       }
     399             : 
     400             :     /* Make sure that we've joined the all-routers multicast group */
     401       12275 :     if (!imd->all_routers_mcast)
     402             :       {
     403             :         /* send MDLP_REPORT_EVENT message */
     404        1793 :         ip6_neighbor_send_mldpv2_report(imd->sw_if_index);
     405        1793 :         imd->all_routers_mcast = 1;
     406             :       }
     407             :   }
     408             :   /* *INDENT-ON* */
     409             : 
     410        7327 :   return 0;
     411             : }
     412             : 
     413             : static uword
     414        7902 : ip6_mld_event_process (vlib_main_t * vm,
     415             :                        vlib_node_runtime_t * node, vlib_frame_t * frame)
     416             : {
     417             :   uword event_type;
     418             : 
     419             :   /* init code here */
     420             : 
     421             :   while (1)
     422             :     {
     423        7902 :       vlib_process_wait_for_event_or_clock (vm, 1. /* seconds */ );
     424             : 
     425        7327 :       if (!vlib_process_get_event_data (vm, &event_type))
     426             :         {
     427             :           /* No events found: timer expired. */
     428             :           /* process interface list and send RAs as appropriate, update timer info */
     429        7327 :           ip6_mld_timer_event (vm, node, frame);
     430             :         }
     431             :       /* else; no events */
     432             :     }
     433             :   return frame->n_vectors;
     434             : }
     435             : 
     436             : /* *INDENT-OFF* */
     437      183788 : VLIB_REGISTER_NODE (ip6_mld_event_process_node) = {
     438             :   .function = ip6_mld_event_process,
     439             :   .name = "ip6-mld-process",
     440             :   .type = VLIB_NODE_TYPE_PROCESS,
     441             : };
     442             : /* *INDENT-ON* */
     443             : 
     444             : static u8 *
     445           0 : format_ip6_mld (u8 * s, va_list * args)
     446             : {
     447           0 :   index_t imi = va_arg (*args, index_t);
     448           0 :   u32 indent = va_arg (*args, u32);
     449             :   ip6_mldp_group_t *m;
     450             :   ip6_mld_t *imd;
     451             : 
     452           0 :   imd = pool_elt_at_index (ip6_mld_pool, imi);
     453             : 
     454           0 :   s = format (s, "%UJoined group address(es):\n", format_white_space, indent);
     455             : 
     456             :   /* *INDENT-OFF* */
     457           0 :   pool_foreach (m, imd->mldp_group_pool)
     458             :    {
     459           0 :     s = format (s, "%U%U\n",
     460             :                 format_white_space, indent+2,
     461             :                 format_ip6_address,
     462             :                 &m->mcast_address);
     463             :   }
     464             :   /* *INDENT-ON* */
     465             : 
     466           0 :   return (s);
     467             : }
     468             : 
     469             : /**
     470             :  * @brief callback when an interface address is added or deleted
     471             :  */
     472             : static void
     473        1988 : ip6_mld_address_add (u32 imi,
     474             :                      const ip6_address_t * address, u8 address_oength)
     475             : {
     476             :   ip6_mld_t *imd;
     477             :   ip6_address_t a;
     478             : 
     479        1988 :   imd = pool_elt_at_index (ip6_mld_pool, imi);
     480             : 
     481             :   /* create solicited node multicast address for this interface address */
     482        1988 :   ip6_set_solicited_node_multicast_address (&a, 0);
     483             : 
     484        1988 :   a.as_u8[0xd] = address->as_u8[0xd];
     485        1988 :   a.as_u8[0xe] = address->as_u8[0xe];
     486        1988 :   a.as_u8[0xf] = address->as_u8[0xf];
     487             : 
     488        1988 :   ip6_neighbor_add_mld_prefix (imd, &a);
     489        1988 : }
     490             : 
     491             : static void
     492        1822 : ip6_mld_address_del (u32 imi,
     493             :                      const ip6_address_t * address, u8 address_oength)
     494             : {
     495             :   ip6_mld_t *imd;
     496             :   ip6_address_t a;
     497             : 
     498        1822 :   imd = pool_elt_at_index (ip6_mld_pool, imi);
     499             : 
     500             :   /* create solicited node multicast address for this interface address */
     501        1822 :   ip6_set_solicited_node_multicast_address (&a, 0);
     502             : 
     503        1822 :   a.as_u8[0xd] = address->as_u8[0xd];
     504        1822 :   a.as_u8[0xe] = address->as_u8[0xe];
     505        1822 :   a.as_u8[0xf] = address->as_u8[0xf];
     506             : 
     507        1822 :   ip6_neighbor_del_mld_prefix (imd, &a);
     508        1822 : }
     509             : 
     510             : /**
     511             :  * VFT for registering as a delegate to an IP6 link
     512             :  */
     513             : const static ip6_link_delegate_vft_t ip6_mld_delegate_vft = {
     514             :   .ildv_disable = ip6_mld_delegate_disable,
     515             :   .ildv_enable = ip6_mld_link_enable,
     516             :   .ildv_format = format_ip6_mld,
     517             :   .ildv_addr_add = ip6_mld_address_add,
     518             :   .ildv_addr_del = ip6_mld_address_del,
     519             : };
     520             : 
     521             : static clib_error_t *
     522         575 : ip6_mld_init (vlib_main_t * vm)
     523             : {
     524         575 :   ip6_mld_delegate_id = ip6_link_delegate_register (&ip6_mld_delegate_vft);
     525             : 
     526         575 :   return (NULL);
     527             : }
     528             : 
     529             : /* *INDENT-OFF* */
     530       96767 : VLIB_INIT_FUNCTION (ip6_mld_init) =
     531             : {
     532             :   .runs_after = VLIB_INITS("icmp6_init"),
     533             : };
     534             : /* *INDENT-ON* */
     535             : 
     536             : /*
     537             :  * fd.io coding-style-patch-verification: ON
     538             :  *
     539             :  * Local Variables:
     540             :  * eval: (c-set-style "gnu")
     541             :  * End:
     542             :  */

Generated by: LCOV version 1.14