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

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * Copyright (c) 2017 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/plugin/plugin.h>
      20             : #include <vpp/app/version.h>
      21             : #include <vnet/ip/ip.h>
      22             : #include <vnet/mfib/mfib_entry.h>
      23             : #include <vlib/unix/unix.h>
      24             : #include <vnet/adj/adj_mcast.h>
      25             : #include <vnet/fib/fib_entry.h>
      26             : #include <vnet/fib/fib_table.h>
      27             : #include <vnet/mfib/mfib_table.h>
      28             : 
      29             : #include <igmp/igmp.h>
      30             : #include <igmp/igmp_format.h>
      31             : #include <igmp/igmp_pkt.h>
      32             : 
      33             : #include <limits.h>
      34             : #include <float.h>
      35             : 
      36             : igmp_main_t igmp_main;
      37             : 
      38             : /* *INDENT-OFF* */
      39             : /* General Query address */
      40             : const static mfib_prefix_t mpfx_general_query = {
      41             :   .fp_proto = FIB_PROTOCOL_IP4,
      42             :   .fp_len = 32,
      43             :   .fp_grp_addr = {
      44             :     .ip4 = {
      45             :       .as_u32 = IGMP_GENERAL_QUERY_ADDRESS,
      46             :     },
      47             :   },
      48             : };
      49             : 
      50             : /* Report address */
      51             : const static mfib_prefix_t mpfx_report = {
      52             :   .fp_proto = FIB_PROTOCOL_IP4,
      53             :   .fp_len = 32,
      54             :   .fp_grp_addr = {
      55             :     .ip4 = {
      56             :       .as_u32 = IGMP_MEMBERSHIP_REPORT_ADDRESS,
      57             :     },
      58             :   },
      59             : };
      60             : /* *INDENT-ON* */
      61             : 
      62             : /**
      63             :  * @brief igmp send query (igmp_timer_function_t)
      64             :  *
      65             :  *   Send an igmp query.
      66             :  *   If the timer holds group key, send Group-Specific query,
      67             :  *   else send General query.
      68             :  */
      69             : static void
      70          14 : igmp_send_general_query (u32 obj, void *dat)
      71             : {
      72             :   igmp_pkt_build_query_t bq;
      73             :   igmp_config_t *config;
      74             : 
      75          14 :   config = igmp_config_get (obj);
      76             : 
      77          14 :   IGMP_DBG ("send-general-query: %U",
      78             :             format_vnet_sw_if_index_name, vnet_get_main (),
      79             :             config->sw_if_index);
      80             : 
      81          14 :   igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY]);
      82             : 
      83          14 :   igmp_pkt_build_query_init (&bq, config->sw_if_index);
      84          14 :   igmp_pkt_query_v3_add_group (&bq, NULL, NULL);
      85          14 :   igmp_pkt_query_v3_send (&bq);
      86             : 
      87             :   /*
      88             :    * re-schedule
      89             :    */
      90          14 :   config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] =
      91          14 :     igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY),
      92             :                          igmp_config_index (config),
      93             :                          igmp_send_general_query, NULL);
      94          14 : }
      95             : 
      96             : static void
      97          20 : igmp_send_state_change_group_report_v3 (u32 sw_if_index,
      98             :                                         const igmp_group_t * group)
      99             : {
     100             :   igmp_pkt_build_report_t br;
     101             : 
     102          20 :   IGMP_DBG ("state-change-group: %U", format_igmp_key, group->key);
     103             : 
     104          20 :   igmp_pkt_build_report_init (&br, sw_if_index);
     105          20 :   igmp_pkt_report_v3_add_group (&br,
     106             :                                 group,
     107             :                                 IGMP_MEMBERSHIP_GROUP_allow_new_sources);
     108          20 :   igmp_pkt_report_v3_send (&br);
     109          20 : }
     110             : 
     111             : static void
     112          10 : igmp_resend_state_change_group_report_v3 (u32 gi, void *data)
     113             : {
     114             :   igmp_config_t *config;
     115             :   igmp_group_t *group;
     116             : 
     117          10 :   group = igmp_group_get (gi);
     118          10 :   config = igmp_config_get (group->config);
     119             : 
     120          10 :   igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]);
     121          10 :   igmp_send_state_change_group_report_v3 (config->sw_if_index, group);
     122             : 
     123          10 :   if (++group->n_reports_sent < config->robustness_var)
     124             :     {
     125           0 :       group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] =
     126           0 :         igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL),
     127             :                              igmp_group_index (group),
     128             :                              igmp_resend_state_change_group_report_v3, NULL);
     129             :     }
     130          10 : }
     131             : 
     132             : int
     133          21 : igmp_listen (vlib_main_t * vm,
     134             :              igmp_filter_mode_t mode,
     135             :              u32 sw_if_index,
     136             :              const ip46_address_t * saddrs, const ip46_address_t * gaddr)
     137             : {
     138             :   const ip46_address_t *saddr;
     139             :   igmp_config_t *config;
     140             :   igmp_group_t *group;
     141             : 
     142             :   /*
     143             :    * RFC 3376 Section 2
     144             :    " For a given combination of socket, interface, and multicast address,
     145             :    * only a single filter mode and source list can be in effect at any one
     146             :    * time.  However, either the filter mode or the source list, or both,
     147             :    * may be changed by subsequent IPMulticastListen requests that specify
     148             :    * the same socket, interface, and multicast address.  Each subsequent
     149             :    * request completely replaces any earlier request for the given socket,
     150             :    * interface and multicast address."
     151             :    */
     152          21 :   int rv = 0;
     153          21 :   IGMP_DBG ("listen: (%U, %U) %U %U",
     154             :             format_igmp_src_addr_list, saddrs,
     155             :             format_igmp_key, gaddr,
     156             :             format_vnet_sw_if_index_name, vnet_get_main (),
     157             :             sw_if_index, format_igmp_filter_mode, mode);
     158             :   /*
     159             :    * find configuration, if it doesn't exist, then this interface is
     160             :    * not IGMP enabled
     161             :    */
     162          21 :   config = igmp_config_lookup (sw_if_index);
     163             : 
     164          21 :   if (!config)
     165             :     {
     166           0 :       rv = VNET_API_ERROR_INVALID_INTERFACE;
     167           0 :       goto error;
     168             :     }
     169          21 :   if (config->mode != IGMP_MODE_HOST)
     170             :     {
     171           0 :       rv = VNET_API_ERROR_INVALID_INTERFACE;
     172           0 :       goto error;
     173             :     }
     174             : 
     175             :   /* find igmp group, if it doesn't exist, create new */
     176          21 :   group = igmp_group_lookup (config, gaddr);
     177             : 
     178          21 :   if (!group)
     179             :     {
     180          10 :       group = igmp_group_alloc (config, gaddr, mode);
     181             : 
     182             :       /* new group implies create all sources */
     183         426 :       vec_foreach (saddr, saddrs)
     184             :       {
     185         416 :         igmp_group_src_update (group, saddr, IGMP_MODE_HOST);
     186             :       }
     187             : 
     188             :       /*
     189             :        * Send state changed event report for the group.
     190             :        *
     191             :        * RFC3376 Section 5.1
     192             :        *  "To cover the possibility of the State-Change Report being missed by
     193             :        *   one or more multicast routers, it is retransmitted [Robustness
     194             :        *   Variable] - 1 more times, at intervals chosen at random from the
     195             :        *   range (0, [Unsolicited Report Interval])."
     196             :        */
     197          10 :       igmp_send_state_change_group_report_v3 (config->sw_if_index, group);
     198             : 
     199          10 :       igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_RESEND_REPORT]);
     200             : 
     201          10 :       group->n_reports_sent = 1;
     202          10 :       group->timers[IGMP_GROUP_TIMER_RESEND_REPORT] =
     203          10 :         igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_REPORT_INTERVAL),
     204             :                              igmp_group_index (group),
     205             :                              igmp_resend_state_change_group_report_v3, NULL);
     206             :     }
     207             :   else
     208             :     {
     209          11 :       IGMP_DBG ("... update (%U, %U) %U %U",
     210             :                 format_igmp_src_addr_list, saddrs,
     211             :                 format_igmp_key, gaddr,
     212             :                 format_vnet_sw_if_index_name, vnet_get_main (),
     213             :                 sw_if_index, format_igmp_filter_mode, mode);
     214             : 
     215             :       /*
     216             :        * RFC 3367 Section 5.1
     217             :        *
     218             :        *   Old State         New State         State-Change Record Sent
     219             :        *   ---------         ---------         ------------------------
     220             :        *
     221             :        * 1) INCLUDE (A)       INCLUDE (B)       ALLOW (B-A), BLOCK (A-B)
     222             :        * 2) EXCLUDE (A)       EXCLUDE (B)       ALLOW (A-B), BLOCK (B-A)
     223             :        * 3) INCLUDE (A)       EXCLUDE (B)       TO_EX (B)
     224             :        * 4) EXCLUDE (A)       INCLUDE (B)       TO_IN (B)
     225             :        *
     226             :        * N.B. We do not split state-change records for pending transfer
     227             :        * hence there is no merge logic required.
     228             :        */
     229             : 
     230          11 :       if (IGMP_FILTER_MODE_INCLUDE == mode)
     231             :         {
     232             :           ip46_address_t *added, *removed;
     233             :           igmp_pkt_build_report_t br;
     234             : 
     235             :           /*
     236             :            * find the list of sources that have been added and removed from
     237             :            * the include set
     238             :            */
     239          11 :           removed =
     240          11 :             igmp_group_present_minus_new (group, IGMP_FILTER_MODE_INCLUDE,
     241             :                                           saddrs);
     242          11 :           added =
     243          11 :             igmp_group_new_minus_present (group, IGMP_FILTER_MODE_INCLUDE,
     244             :                                           saddrs);
     245             : 
     246          11 :           if (!(vec_len (added) || vec_len (removed)))
     247             :             /* no change => done */
     248           0 :             goto error;
     249             : 
     250          11 :           igmp_pkt_build_report_init (&br, config->sw_if_index);
     251             : 
     252          11 :           if (vec_len (added))
     253             :             {
     254           1 :               igmp_pkt_report_v3_add_report (&br,
     255           1 :                                              group->key,
     256             :                                              added,
     257             :                                              IGMP_MEMBERSHIP_GROUP_allow_new_sources);
     258             :             }
     259             : 
     260          11 :           if (vec_len (removed))
     261             :             {
     262          11 :               igmp_pkt_report_v3_add_report (&br,
     263          11 :                                              group->key,
     264             :                                              removed,
     265             :                                              IGMP_MEMBERSHIP_GROUP_block_old_sources);
     266             :             }
     267             : 
     268          11 :           IGMP_DBG ("... added %U", format_igmp_src_addr_list, added);
     269          11 :           IGMP_DBG ("... removed %U", format_igmp_src_addr_list, removed);
     270             : 
     271          11 :           igmp_pkt_report_v3_send (&br);
     272             : 
     273             :           /*
     274             :            * clear the group of the old sources and populate it with the new
     275             :            * set requested
     276             :            */
     277          11 :           igmp_group_free_all_srcs (group);
     278             : 
     279          27 :           vec_foreach (saddr, saddrs)
     280             :           {
     281          16 :             igmp_group_src_update (group, saddr, IGMP_MODE_HOST);
     282             :           }
     283             : 
     284          11 :           if (0 == igmp_group_n_srcs (group, mode))
     285          10 :             igmp_group_clear (&group);
     286             : 
     287          11 :           vec_free (added);
     288          11 :           vec_free (removed);
     289             :         }
     290             :       else
     291             :         {
     292             :           /*
     293             :            * The control plane is excluding some sources.
     294             :            *  - First; check for those that are present in the include list
     295             :            *  - Second; check add them to the exclude list
     296             :            *
     297             :            * TODO
     298             :            */
     299             :         }
     300             :     }
     301             : 
     302           0 : error:
     303          21 :   return (rv);
     304             : }
     305             : 
     306             : static walk_rc_t
     307        6347 : igmp_sw_if_down (vnet_main_t * vnm, u32 sw_if_index, void *ctx)
     308             : {
     309             :   igmp_config_t *config;
     310        6347 :   config = igmp_config_lookup (sw_if_index);
     311        6347 :   IGMP_DBG ("down: %U",
     312             :             format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index);
     313        6347 :   if (NULL != config)
     314             :     {
     315           0 :       igmp_clear_config (config);
     316             :     }
     317             : 
     318        6347 :   return (WALK_CONTINUE);
     319             : }
     320             : 
     321             : /** \brief igmp hardware interface link up down
     322             :     @param vnm - vnet main
     323             :     @param hw_if_index - interface hw_if_index
     324             :     @param flags - hw interface flags
     325             : 
     326             :     If an interface goes down, remove its (S,G)s.
     327             : */
     328             : static clib_error_t *
     329       13092 : igmp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
     330             : {
     331       13092 :   clib_error_t *error = NULL;
     332             :   /* remove igmp state from down interfaces */
     333       13092 :   if (!(flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
     334        6315 :     vnet_hw_interface_walk_sw (vnm, hw_if_index, igmp_sw_if_down, NULL);
     335       13092 :   return error;
     336             : }
     337             : 
     338        2241 : VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (igmp_hw_interface_link_up_down);
     339             : int
     340          18 : igmp_enable_disable (u32 sw_if_index, u8 enable, igmp_mode_t mode)
     341             : {
     342             :   igmp_config_t *config;
     343          18 :   igmp_main_t *im = &igmp_main;
     344             :   u32 mfib_index;
     345          18 :   IGMP_DBG ("%s:  %U", (enable ? "Enabled" : "Disabled"),
     346             :             format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index);
     347             : 
     348             :   /* *INDENT-OFF* */
     349          36 :   fib_route_path_t via_itf_path =
     350             :     {
     351          18 :       .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
     352             :       .frp_addr = zero_addr,
     353             :       .frp_sw_if_index = sw_if_index,
     354             :       .frp_fib_index = 0,
     355             :       .frp_weight = 1,
     356             :     .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
     357             :     };
     358          36 :   fib_route_path_t for_us_path = {
     359          18 :     .frp_proto = fib_proto_to_dpo (FIB_PROTOCOL_IP4),
     360             :     .frp_addr = zero_addr,
     361             :     .frp_sw_if_index = 0xffffffff,
     362             :     .frp_fib_index = 1,
     363             :     .frp_weight = 0,
     364             :     .frp_flags = FIB_ROUTE_PATH_LOCAL,
     365             :     .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
     366             :   };
     367             : 
     368             :   /* *INDENT-ON* */
     369             :   /* find configuration, if it doesn't exist, create new */
     370          18 :   config = igmp_config_lookup (sw_if_index);
     371          18 :   mfib_index = mfib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
     372             :                                                      sw_if_index);
     373          18 :   if (!config && enable)
     374           9 :     {
     375             :       u32 ii;
     376             : 
     377          14 :       vec_validate_init_empty (im->igmp_config_by_sw_if_index,
     378             :                                sw_if_index, ~0);
     379           9 :       pool_get (im->configs, config);
     380           9 :       clib_memset (config, 0, sizeof (igmp_config_t));
     381           9 :       config->sw_if_index = sw_if_index;
     382           9 :       config->igmp_group_by_key =
     383           9 :         hash_create_mem (0, sizeof (igmp_key_t), sizeof (uword));
     384           9 :       config->robustness_var = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
     385           9 :       config->mode = mode;
     386           9 :       config->proxy_device_id = ~0;
     387             : 
     388          27 :       for (ii = 0; ii < IGMP_CONFIG_N_TIMERS; ii++)
     389          18 :         config->timers[ii] = IGMP_TIMER_ID_INVALID;
     390             : 
     391           9 :       if (IGMP_MODE_ROUTER == mode)
     392             :         {
     393           3 :           config->timers[IGMP_CONFIG_TIMER_GENERAL_QUERY] =
     394           3 :             igmp_timer_schedule (igmp_timer_type_get (IGMP_TIMER_QUERY),
     395             :                                  igmp_config_index (config),
     396             :                                  igmp_send_general_query, NULL);
     397             :         }
     398             : 
     399          18 :       config->adj_index =
     400           9 :         adj_mcast_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4,
     401           9 :                                config->sw_if_index);
     402           9 :       im->igmp_config_by_sw_if_index[config->sw_if_index] =
     403           9 :         (config - im->configs);
     404             :       {
     405           9 :         vec_validate (im->n_configs_per_mfib_index, mfib_index);
     406           9 :         im->n_configs_per_mfib_index[mfib_index]++;
     407           9 :         if (1 == im->n_configs_per_mfib_index[mfib_index])
     408             :           {
     409             :             /* first config in this FIB */
     410           5 :             mfib_table_lock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
     411           5 :             mfib_table_entry_path_update (mfib_index, &mpfx_general_query,
     412             :                                           MFIB_SOURCE_IGMP,
     413             :                                           MFIB_ENTRY_FLAG_NONE, &for_us_path);
     414           5 :             mfib_table_entry_path_update (mfib_index, &mpfx_report,
     415             :                                           MFIB_SOURCE_IGMP,
     416             :                                           MFIB_ENTRY_FLAG_NONE, &for_us_path);
     417             :           }
     418           9 :         mfib_table_entry_path_update (mfib_index, &mpfx_general_query,
     419             :                                       MFIB_SOURCE_IGMP, MFIB_ENTRY_FLAG_NONE,
     420             :                                       &via_itf_path);
     421           9 :         mfib_table_entry_path_update (mfib_index, &mpfx_report,
     422             :                                       MFIB_SOURCE_IGMP, MFIB_ENTRY_FLAG_NONE,
     423             :                                       &via_itf_path);
     424             :       }
     425             :     }
     426           9 :   else if (config && !enable)
     427             :     {
     428           9 :       vec_validate (im->n_configs_per_mfib_index, mfib_index);
     429           9 :       im->n_configs_per_mfib_index[mfib_index]--;
     430           9 :       if (0 == im->n_configs_per_mfib_index[mfib_index])
     431             :         {
     432             :           /* last config in this FIB */
     433           5 :           mfib_table_entry_path_remove (mfib_index,
     434             :                                         &mpfx_general_query,
     435             :                                         MFIB_SOURCE_IGMP, &for_us_path);
     436           5 :           mfib_table_entry_path_remove (mfib_index,
     437             :                                         &mpfx_report,
     438             :                                         MFIB_SOURCE_IGMP, &for_us_path);
     439           5 :           mfib_table_unlock (mfib_index, FIB_PROTOCOL_IP4, MFIB_SOURCE_IGMP);
     440             :         }
     441             : 
     442           9 :       mfib_table_entry_path_remove (mfib_index,
     443             :                                     &mpfx_general_query,
     444             :                                     MFIB_SOURCE_IGMP, &via_itf_path);
     445           9 :       mfib_table_entry_path_remove (mfib_index,
     446             :                                     &mpfx_report,
     447             :                                     MFIB_SOURCE_IGMP, &via_itf_path);
     448             : 
     449             :       /*
     450             :        * remove interface from proxy device
     451             :        * if this device is upstream, delete proxy device
     452             :        */
     453           9 :       if (config->mode == IGMP_MODE_ROUTER)
     454           3 :         igmp_proxy_device_add_del_interface (config->proxy_device_id,
     455           3 :                                              config->sw_if_index, 0);
     456           6 :       else if (config->mode == IGMP_MODE_HOST)
     457           6 :         igmp_proxy_device_add_del (config->proxy_device_id,
     458           6 :                                    config->sw_if_index, 0);
     459             : 
     460           9 :       igmp_clear_config (config);
     461           9 :       im->igmp_config_by_sw_if_index[config->sw_if_index] = ~0;
     462           9 :       hash_free (config->igmp_group_by_key);
     463           9 :       pool_put (im->configs, config);
     464             :     }
     465             :   else
     466             :     {
     467           0 :       return -1;
     468             :     }
     469             : 
     470          18 :   return (0);
     471             : }
     472             : 
     473             : /** \brief igmp initialization
     474             :     @param vm - vlib main
     475             : 
     476             :     initialize igmp plugin. Initialize igmp_main, set mfib to allow igmp traffic.
     477             : */
     478             : static clib_error_t *
     479         559 : igmp_init (vlib_main_t * vm)
     480             : {
     481         559 :   igmp_main_t *im = &igmp_main;
     482             : 
     483         559 :   im->igmp_api_client_by_client_index = hash_create (0, sizeof (u32));
     484         559 :   im->logger = vlib_log_register_class ("igmp", 0);
     485             : 
     486         559 :   IGMP_DBG ("initialized");
     487             : 
     488         559 :   return (0);
     489             : }
     490             : 
     491             : /* *INDENT-OFF* */
     492        1119 : VLIB_INIT_FUNCTION (igmp_init) =
     493             : {
     494             :   .runs_after = VLIB_INITS("ip4_lookup_init"),
     495             : };
     496             : VLIB_PLUGIN_REGISTER () =
     497             : {
     498             :   .version = VPP_BUILD_VER,
     499             :   .description = "Internet Group Management Protocol (IGMP)",
     500             : };
     501             : /* *INDENT-ON* */
     502             : 
     503             : /*
     504             :  * fd.io coding-style-patch-verification: ON
     505             :  *
     506             :  * Local Variables:
     507             :  * eval: (c-set-style "gnu")
     508             :  * End:
     509             :  */

Generated by: LCOV version 1.14