LCOV - code coverage report
Current view: top level - plugins/igmp - igmp_proxy.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 147 170 86.5 %
Date: 2023-07-05 22:20:52 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * Copyright (c) 2018 Cisco and/or its affiliates.
       4             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5             :  * you may not use this file except in compliance with the License.
       6             :  * You may obtain a copy of the License at:
       7             :  *
       8             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  * Unless required by applicable law or agreed to in writing, software
      11             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  * See the License for the specific language governing permissions and
      14             :  * limitations under the License.
      15             :  *------------------------------------------------------------------
      16             :  */
      17             : 
      18             : #include <vlib/vlib.h>
      19             : #include <vnet/mfib/mfib_entry.h>
      20             : #include <vnet/mfib/mfib_table.h>
      21             : 
      22             : #include <igmp/igmp_proxy.h>
      23             : #include <igmp/igmp.h>
      24             : #include <igmp/igmp_pkt.h>
      25             : 
      26             : void
      27          18 : igmp_proxy_device_mfib_path_add_del (igmp_group_t * group, u8 add)
      28             : {
      29             :   igmp_config_t *config;
      30             :   u32 mfib_index;
      31             : 
      32          18 :   config = igmp_config_get (group->config);
      33             :   mfib_index =
      34          18 :     mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
      35             :                                           config->sw_if_index);
      36             : 
      37             :   /* *INDENT-OFF* */
      38          18 :   mfib_prefix_t mpfx_group_addr = {
      39             :       .fp_proto = FIB_PROTOCOL_IP4,
      40             :       .fp_len = 32,
      41             :       .fp_grp_addr = {
      42          18 :         .ip4 = (*group->key).ip4,
      43             :       },
      44             :     };
      45          36 :   fib_route_path_t via_itf_path =
      46             :     {
      47          18 :       .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
      48             :       .frp_addr = zero_addr,
      49          18 :       .frp_sw_if_index = config->sw_if_index,
      50             :       .frp_fib_index = 0,
      51             :       .frp_weight = 1,
      52             :       .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
      53             :     };
      54             :   /* *INDENT-ON* */
      55             : 
      56          18 :   if (add)
      57           8 :     mfib_table_entry_path_update (mfib_index, &mpfx_group_addr,
      58             :                                   MFIB_SOURCE_IGMP, MFIB_ENTRY_FLAG_NONE,
      59             :                                   &via_itf_path);
      60             :   else
      61          10 :     mfib_table_entry_path_remove (mfib_index, &mpfx_group_addr,
      62             :                                   MFIB_SOURCE_IGMP, &via_itf_path);
      63          18 : }
      64             : 
      65             : igmp_proxy_device_t *
      66          32 : igmp_proxy_device_lookup (u32 vrf_id)
      67             : {
      68          32 :   igmp_main_t *im = &igmp_main;
      69             : 
      70          32 :   if (vec_len (im->igmp_proxy_device_by_vrf_id) > vrf_id)
      71             :     {
      72             :       u32 index;
      73          11 :       index = im->igmp_proxy_device_by_vrf_id[vrf_id];
      74          11 :       if (index != ~0)
      75          11 :         return (vec_elt_at_index (im->proxy_devices, index));
      76             :     }
      77          21 :   return NULL;
      78             : }
      79             : 
      80             : int
      81           7 : igmp_proxy_device_add_del (u32 vrf_id, u32 sw_if_index, u8 add)
      82             : {
      83           7 :   igmp_main_t *im = &igmp_main;
      84             :   igmp_proxy_device_t *proxy_device;
      85             :   igmp_config_t *config;
      86             :   u32 mfib_index;
      87             : 
      88             :   /* check VRF id */
      89             :   mfib_index =
      90           7 :     mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
      91           7 :   if (mfib_index == ~0)
      92           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
      93           7 :   if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
      94           5 :     return VNET_API_ERROR_INVALID_INTERFACE;
      95             : 
      96             :   /* check IGMP configuration */
      97           2 :   config = igmp_config_lookup (sw_if_index);
      98           2 :   if (!config)
      99           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     100           2 :   if (config->mode != IGMP_MODE_HOST)
     101           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     102             : 
     103           2 :   proxy_device = igmp_proxy_device_lookup (vrf_id);
     104           2 :   if (!proxy_device && add)
     105             :     {
     106           2 :       vec_validate_init_empty (im->igmp_proxy_device_by_vrf_id, vrf_id, ~0);
     107           1 :       pool_get (im->proxy_devices, proxy_device);
     108           1 :       im->igmp_proxy_device_by_vrf_id[vrf_id] =
     109           1 :         proxy_device - im->proxy_devices;
     110           1 :       clib_memset (proxy_device, 0, sizeof (igmp_proxy_device_t));
     111           1 :       proxy_device->vrf_id = vrf_id;
     112           1 :       proxy_device->upstream_if = sw_if_index;
     113           1 :       config->proxy_device_id = vrf_id;
     114             :       /* lock mfib table */
     115           1 :       mfib_table_lock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
     116             :     }
     117           1 :   else if (proxy_device && !add)
     118             :     {
     119           2 :       while (vec_len (proxy_device->downstream_ifs) > 0)
     120             :         {
     121           1 :           igmp_proxy_device_add_del_interface (vrf_id,
     122           1 :                                                proxy_device->downstream_ifs
     123             :                                                [0], 0);
     124             :         }
     125           1 :       vec_free (proxy_device->downstream_ifs);
     126           1 :       proxy_device->downstream_ifs = NULL;
     127           1 :       im->igmp_proxy_device_by_vrf_id[vrf_id] = ~0;
     128           1 :       pool_put (im->proxy_devices, proxy_device);
     129           1 :       config->proxy_device_id = ~0;
     130             :       /* clear proxy database */
     131           1 :       igmp_clear_config (config);
     132             :       /* unlock mfib table */
     133           1 :       mfib_table_unlock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
     134             :     }
     135             :   else
     136           0 :     return -1;
     137             : 
     138           2 :   return 0;
     139             : }
     140             : 
     141             : int
     142           6 : igmp_proxy_device_add_del_interface (u32 vrf_id, u32 sw_if_index, u8 add)
     143             : {
     144             :   igmp_proxy_device_t *proxy_device;
     145             :   u32 index;
     146             :   u32 mfib_index;
     147             : 
     148           6 :   proxy_device = igmp_proxy_device_lookup (vrf_id);
     149           6 :   if (!proxy_device)
     150           2 :     return -1;
     151             : 
     152             :   /* check VRF id */
     153             :   mfib_index =
     154           4 :     mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
     155           4 :   if (mfib_index == ~0)
     156           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     157           4 :   if (vrf_id != mfib_table_get (mfib_index, FIB_PROTOCOL_IP4)->mft_table_id)
     158           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     159             : 
     160             :   /* check IGMP configuration */
     161             :   igmp_config_t *config;
     162           4 :   config = igmp_config_lookup (sw_if_index);
     163           4 :   if (!config)
     164           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     165           4 :   if (config->mode != IGMP_MODE_ROUTER)
     166           0 :     return VNET_API_ERROR_INVALID_INTERFACE;
     167             : 
     168           4 :   if (add)
     169             :     {
     170           2 :       if (proxy_device->downstream_ifs)
     171             :         {
     172           2 :           index = vec_search (proxy_device->downstream_ifs, sw_if_index);
     173           1 :           if (index != ~0)
     174           0 :             return -1;
     175             :         }
     176           2 :       vec_add1 (proxy_device->downstream_ifs, sw_if_index);
     177           2 :       config->proxy_device_id = vrf_id;
     178             :     }
     179             :   else
     180             :     {
     181           2 :       if (!proxy_device->downstream_ifs)
     182           0 :         return -2;
     183           2 :       index = vec_search (proxy_device->downstream_ifs, sw_if_index);
     184           2 :       if (index == ~0)
     185           0 :         return -3;
     186             :       /* remove (S,G)s belonging to this interface from proxy database */
     187           2 :       igmp_proxy_device_merge_config (config, /* block */ 1);
     188           2 :       vec_del1 (proxy_device->downstream_ifs, index);
     189           2 :       config->proxy_device_id = ~0;
     190             :     }
     191             : 
     192           4 :   return 0;
     193             : }
     194             : 
     195             : void
     196           9 : igmp_proxy_device_block_src (igmp_config_t * config, igmp_group_t * group,
     197             :                              igmp_src_t * src)
     198             : {
     199             :   igmp_proxy_device_t *proxy_device;
     200             :   igmp_config_t *proxy_config;
     201             :   igmp_group_t *proxy_group;
     202             :   igmp_src_t *proxy_src;
     203             :   u8 *ref;
     204             : 
     205           9 :   proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
     206           9 :   if (!proxy_device)
     207           8 :     return;
     208             : 
     209           1 :   proxy_config = igmp_config_lookup (proxy_device->upstream_if);
     210           1 :   ASSERT (proxy_config);
     211             : 
     212           1 :   proxy_group = igmp_group_lookup (proxy_config, group->key);
     213           1 :   if (proxy_group == NULL)
     214           0 :     return;
     215             : 
     216           1 :   proxy_src = igmp_src_lookup (proxy_group, src->key);
     217           1 :   if (proxy_src == NULL)
     218           0 :     return;
     219             : 
     220           1 :   if (vec_len (proxy_src->referance_by_config_index) <= group->config)
     221             :     {
     222           0 :       IGMP_DBG ("proxy block src: invalid config %u", group->config);
     223           0 :       return;
     224             :     }
     225           1 :   proxy_src->referance_by_config_index[group->config] = 0;
     226           3 :   vec_foreach (ref, proxy_src->referance_by_config_index)
     227             :   {
     228           2 :     if ((*ref) > 0)
     229           0 :       return;
     230             :   }
     231             : 
     232             :   /* build "Block Old Sources" report */
     233             :   igmp_pkt_build_report_t br;
     234           1 :   ip46_address_t *srcaddrs = NULL;
     235             : 
     236           1 :   igmp_pkt_build_report_init (&br, proxy_config->sw_if_index);
     237           1 :   vec_add1 (srcaddrs, *proxy_src->key);
     238           1 :   igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
     239             :                                  IGMP_MEMBERSHIP_GROUP_block_old_sources);
     240           1 :   igmp_pkt_report_v3_send (&br);
     241             : 
     242             : 
     243           1 :   igmp_group_src_remove (proxy_group, proxy_src);
     244           1 :   igmp_src_free (proxy_src);
     245             : 
     246           1 :   if (igmp_group_n_srcs (proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
     247             :     {
     248           0 :       igmp_proxy_device_mfib_path_add_del (proxy_group, 0);
     249           0 :       igmp_proxy_device_mfib_path_add_del (group, 0);
     250           0 :       igmp_group_clear (&proxy_group);
     251             :     }
     252             : }
     253             : 
     254             : always_inline void
     255           9 : igmp_proxy_device_merge_src (igmp_group_t ** proxy_group, igmp_src_t * src,
     256             :                              ip46_address_t ** srcaddrs, u8 block)
     257             : {
     258             :   igmp_src_t *proxy_src;
     259             :   u32 d_config;
     260             : 
     261           9 :   proxy_src = igmp_src_lookup (*proxy_group, src->key);
     262             : 
     263           9 :   if (proxy_src == NULL)
     264             :     {
     265           3 :       if (block)
     266           0 :         return;
     267             :       /* store downstream config index */
     268           3 :       d_config = igmp_group_get (src->group)->config;
     269             : 
     270             :       proxy_src =
     271           3 :         igmp_src_alloc (igmp_group_index (*proxy_group), src->key,
     272             :                         IGMP_MODE_HOST);
     273             : 
     274           6 :       hash_set_mem ((*proxy_group)->igmp_src_by_key
     275             :                     [(*proxy_group)->router_filter_mode], proxy_src->key,
     276             :                     igmp_src_index (proxy_src));
     277             : 
     278          11 :       vec_validate_init_empty (proxy_src->referance_by_config_index, d_config,
     279             :                                0);
     280           3 :       proxy_src->referance_by_config_index[d_config] = 1;
     281           3 :       vec_add1 (*srcaddrs, *proxy_src->key);
     282             :     }
     283             :   else
     284             :     {
     285           6 :       if (block)
     286             :         {
     287           3 :           d_config = igmp_group_get (src->group)->config;
     288           3 :           if (vec_len (proxy_src->referance_by_config_index) <= d_config)
     289             :             {
     290           0 :               IGMP_DBG ("proxy block src: invalid config %u", d_config);
     291           0 :               return;
     292             :             }
     293           3 :           proxy_src->referance_by_config_index[d_config] = 0;
     294             :           u8 *ref;
     295          10 :           vec_foreach (ref, proxy_src->referance_by_config_index)
     296             :           {
     297           8 :             if ((*ref) > 0)
     298           1 :               return;
     299             :           }
     300             : 
     301           2 :           vec_add1 (*srcaddrs, *proxy_src->key);
     302             : 
     303           2 :           igmp_group_src_remove (*proxy_group, proxy_src);
     304           2 :           igmp_src_free (proxy_src);
     305             : 
     306           2 :           if (igmp_group_n_srcs (*proxy_group, IGMP_FILTER_MODE_INCLUDE) == 0)
     307             :             {
     308           1 :               igmp_proxy_device_mfib_path_add_del (*proxy_group, 0);
     309           1 :               igmp_group_clear (proxy_group);
     310             :             }
     311           2 :           return;
     312             :         }
     313           3 :       d_config = igmp_group_get (src->group)->config;
     314           3 :       vec_validate (proxy_src->referance_by_config_index, d_config);
     315           3 :       proxy_src->referance_by_config_index[d_config] = 1;
     316           3 :       return;
     317             :     }
     318             : }
     319             : 
     320             : always_inline igmp_group_t *
     321           5 : igmp_proxy_device_merge_group (igmp_proxy_device_t * proxy_device,
     322             :                                igmp_group_t * group,
     323             :                                ip46_address_t ** srcaddrs, u8 block)
     324             : {
     325             :   igmp_config_t *proxy_config;
     326             :   igmp_group_t *proxy_group;
     327             :   igmp_src_t *src;
     328             : 
     329           5 :   proxy_config = igmp_config_lookup (proxy_device->upstream_if);
     330           5 :   ALWAYS_ASSERT (proxy_config);
     331             : 
     332           5 :   proxy_group = igmp_group_lookup (proxy_config, group->key);
     333           5 :   if (!proxy_group)
     334             :     {
     335           1 :       if (block)
     336           0 :         return NULL;
     337           1 :       u32 tmp = igmp_group_index (group);
     338           1 :       proxy_group =
     339           1 :         igmp_group_alloc (proxy_config, group->key,
     340             :                           group->router_filter_mode);
     341           1 :       igmp_proxy_device_mfib_path_add_del (proxy_group, 1);
     342           1 :       group = igmp_group_get (tmp);
     343             :     }
     344           5 :   if (block)
     345             :     {
     346           2 :       igmp_proxy_device_mfib_path_add_del (group, 0);
     347             :     }
     348             : 
     349             :   /* *INDENT-OFF* */
     350         334 :   FOR_EACH_SRC (src, group, group->router_filter_mode,
     351             :     ({
     352             :       igmp_proxy_device_merge_src (&proxy_group, src, srcaddrs, block);
     353             :     }));
     354             :   /* *INDENT-ON* */
     355           5 :   return proxy_group;
     356             : }
     357             : 
     358             : void
     359          15 : igmp_proxy_device_merge_config (igmp_config_t * config, u8 block)
     360             : {
     361             :   igmp_proxy_device_t *proxy_device;
     362             :   igmp_group_t *group;
     363             :   igmp_group_t *proxy_group;
     364          15 :   ip46_address_t *srcaddrs = NULL;
     365             :   igmp_pkt_build_report_t br;
     366             : 
     367          15 :   proxy_device = igmp_proxy_device_lookup (config->proxy_device_id);
     368          15 :   if (!proxy_device)
     369          10 :     return;
     370             : 
     371           5 :   igmp_pkt_build_report_init (&br, proxy_device->upstream_if);
     372             : 
     373             :   /* *INDENT-OFF* */
     374         330 :   FOR_EACH_GROUP(group, config,
     375             :     ({
     376             :       proxy_group = igmp_proxy_device_merge_group (proxy_device, group, &srcaddrs, block);
     377             : 
     378             :       if ((vec_len(srcaddrs) > 0) && proxy_group)
     379             :         {
     380             :           igmp_pkt_report_v3_add_report (&br, proxy_group->key, srcaddrs,
     381             :                                          block ? IGMP_MEMBERSHIP_GROUP_block_old_sources :
     382             :                                          IGMP_MEMBERSHIP_GROUP_allow_new_sources);
     383             :         }
     384             :       vec_free (srcaddrs);
     385             :     }));
     386             :   /* *INDENT-ON* */
     387             : 
     388           5 :   igmp_pkt_report_v3_send (&br);
     389             : 
     390             : }
     391             : 
     392             : /*
     393             :  * fd.io coding-style-patch-verification: ON
     394             :  *
     395             :  * Local Variables:
     396             :  * eval: (c-set-style "gnu")
     397             :  * End:
     398             :  */

Generated by: LCOV version 1.14