LCOV - code coverage report
Current view: top level - plugins/igmp - igmp_query.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 76 82 92.7 %
Date: 2023-10-26 01:39:38 Functions: 6 6 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 <igmp/igmp_query.h>
      19             : #include <igmp/igmp_pkt.h>
      20             : 
      21             : static f64
      22          11 : igmp_get_random_resp_delay (const igmp_header_t * header)
      23             : {
      24             :   u32 seed;
      25             : 
      26          11 :   seed = vlib_time_now (vlib_get_main ());
      27             : 
      28          11 :   return ((random_f64 (&seed) * igmp_header_get_max_resp_time (header)));
      29             : 
      30             : }
      31             : 
      32             : static ip46_address_t *
      33           9 : igmp_query_mk_source_list (const igmp_membership_query_v3_t * q)
      34             : {
      35           9 :   ip46_address_t *srcs = NULL;
      36             :   const ip4_address_t *s;
      37             :   u16 ii, n;
      38             : 
      39             :   /*
      40             :    * we validated this packet when we accepted it in the DP, so
      41             :    * this number is safe to use
      42             :    */
      43           9 :   n = clib_net_to_host_u16 (q->n_src_addresses);
      44             : 
      45           9 :   if (0 == n)
      46           1 :     return (NULL);
      47             : 
      48           8 :   vec_validate (srcs, n - 1);
      49           8 :   s = q->src_addresses;
      50             : 
      51          20 :   for (ii = 0; ii < n; ii++)
      52             :     {
      53          12 :       srcs[ii].ip4 = *s;
      54          12 :       s++;
      55             :     }
      56             : 
      57           8 :   return (srcs);
      58             : }
      59             : 
      60             : static void
      61           7 : igmp_send_group_report_v3 (u32 obj, void *data)
      62             : {
      63             :   igmp_pkt_build_report_t br;
      64             :   igmp_config_t *config;
      65             :   ip46_address_t *srcs;
      66             :   igmp_group_t *group;
      67             :   igmp_main_t *im;
      68             : 
      69           7 :   im = &igmp_main;
      70           7 :   srcs = data;
      71           7 :   group = pool_elt_at_index (im->groups, obj);
      72           7 :   config = pool_elt_at_index (im->configs, group->config);
      73             : 
      74           7 :   igmp_pkt_build_report_init (&br, config->sw_if_index);
      75           7 :   ASSERT (group->timers[IGMP_GROUP_TIMER_QUERY_REPLY] !=
      76             :           IGMP_TIMER_ID_INVALID);
      77             : 
      78           7 :   IGMP_DBG ("send-group-report: %U",
      79             :             format_vnet_sw_if_index_name,
      80             :             vnet_get_main (), config->sw_if_index);
      81             : 
      82           7 :   if (NULL == srcs)
      83             :     {
      84             :       /*
      85             :        * there were no sources specified, so this is a group-specific query.
      86             :        * We should respond with all our sources
      87             :        */
      88           1 :       igmp_pkt_report_v3_add_group (&br, group,
      89             :                                     IGMP_MEMBERSHIP_GROUP_mode_is_include);
      90             :     }
      91             :   else
      92             :     {
      93             :       /*
      94             :        * the sources stored in the timer object are the combined set of sources
      95             :        * to be required. We need to respond only to those queried, not our full set.
      96             :        */
      97             :       ip46_address_t *intersect;
      98             : 
      99           6 :       intersect = igmp_group_new_intersect_present (group,
     100             :                                                     IGMP_FILTER_MODE_INCLUDE,
     101             :                                                     srcs);
     102             : 
     103           6 :       if (vec_len (intersect))
     104             :         {
     105           5 :           igmp_pkt_report_v3_add_report (&br,
     106           5 :                                          group->key,
     107             :                                          intersect,
     108             :                                          IGMP_MEMBERSHIP_GROUP_mode_is_include);
     109           5 :           vec_free (intersect);
     110             :         }
     111             :     }
     112             : 
     113           7 :   igmp_pkt_report_v3_send (&br);
     114             : 
     115           7 :   igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_QUERY_REPLY]);
     116           7 :   vec_free (srcs);
     117           7 : }
     118             : 
     119             : static igmp_membership_group_v3_type_t
     120          16 : igmp_filter_mode_to_report_type (igmp_filter_mode_t mode)
     121             : {
     122          16 :   switch (mode)
     123             :     {
     124          16 :     case IGMP_FILTER_MODE_INCLUDE:
     125          16 :       return (IGMP_MEMBERSHIP_GROUP_mode_is_include);
     126           0 :     case IGMP_FILTER_MODE_EXCLUDE:
     127           0 :       return (IGMP_MEMBERSHIP_GROUP_mode_is_exclude);
     128             :     }
     129             : 
     130           0 :   return (IGMP_MEMBERSHIP_GROUP_mode_is_include);
     131             : }
     132             : 
     133             : /**
     134             :  * Send igmp membership general report.
     135             :  */
     136             : static void
     137           4 : igmp_send_general_report_v3 (u32 obj, void *data)
     138             : {
     139             :   igmp_pkt_build_report_t br;
     140             :   igmp_config_t *config;
     141             :   igmp_group_t *group;
     142             :   igmp_main_t *im;
     143             : 
     144           4 :   im = &igmp_main;
     145           4 :   config = pool_elt_at_index (im->configs, obj);
     146             : 
     147           4 :   ASSERT (config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] !=
     148             :           IGMP_TIMER_ID_INVALID);
     149             : 
     150           4 :   igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT]);
     151             : 
     152           4 :   IGMP_DBG ("send-general-report: %U",
     153             :             format_vnet_sw_if_index_name,
     154             :             vnet_get_main (), config->sw_if_index);
     155             : 
     156           4 :   igmp_pkt_build_report_init (&br, config->sw_if_index);
     157             : 
     158             :   /* *INDENT-OFF* */
     159         276 :   FOR_EACH_GROUP (group, config,
     160             :     ({
     161             :       igmp_pkt_report_v3_add_group
     162             :         (&br, group,
     163             :          igmp_filter_mode_to_report_type(group->router_filter_mode));
     164             :     }));
     165             :   /* *INDENT-ON* */
     166             : 
     167           4 :   igmp_pkt_report_v3_send (&br);
     168           4 : }
     169             : 
     170             : /**
     171             :  * Called from the main thread on reception of a Query message
     172             :  */
     173             : void
     174          13 : igmp_handle_query (const igmp_query_args_t * args)
     175             : {
     176             :   igmp_config_t *config;
     177             : 
     178          13 :   config = igmp_config_lookup (args->sw_if_index);
     179             : 
     180          13 :   if (!config)
     181             :     /*
     182             :      * no IGMP config on the interface. quit
     183             :      */
     184           0 :     return;
     185             : 
     186          13 :   if (IGMP_MODE_ROUTER == config->mode)
     187             :     {
     188           0 :       ASSERT (0);
     189             :       // code here for querier election */
     190             :     }
     191             : 
     192          13 :   IGMP_DBG ("query-rx: %U", format_vnet_sw_if_index_name,
     193             :             vnet_get_main (), args->sw_if_index);
     194             : 
     195             : 
     196             :   /*
     197             :      Section 5.2
     198             :      "When a system receives a Query, it does not respond immediately.
     199             :      Instead, it delays its response by a random amount of time, bounded
     200             :      by the Max Resp Time value derived from the Max Resp Code in the
     201             :      received Query message.  A system may receive a variety of Queries on
     202             :      different interfaces and of different kinds (e.g., General Queries,
     203             :      Group-Specific Queries, and Group-and-Source-Specific Queries), each
     204             :      of which may require its own delayed response.
     205             :    */
     206          13 :   if (igmp_membership_query_v3_is_general (args->query))
     207             :     {
     208           4 :       IGMP_DBG ("...general-query-rx: %U", format_vnet_sw_if_index_name,
     209             :                 vnet_get_main (), args->sw_if_index);
     210             : 
     211             :       /*
     212             :        * A general query has no info that needs saving from the response
     213             :        */
     214           4 :       if (IGMP_TIMER_ID_INVALID ==
     215           4 :           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT])
     216             :         {
     217           4 :           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
     218             : 
     219           4 :           IGMP_DBG ("...general-query-rx: %U schedule for %f",
     220             :                     format_vnet_sw_if_index_name, vnet_get_main (),
     221             :                     args->sw_if_index, delay);
     222             : 
     223             :           /*
     224             :            * no currently running timer, schedule a new one
     225             :            */
     226           4 :           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] =
     227           4 :             igmp_timer_schedule (delay,
     228             :                                  igmp_config_index (config),
     229             :                                  igmp_send_general_report_v3, NULL);
     230             :         }
     231             :       /*
     232             :        * else
     233             :        *  don't reschedule timers, we'll reply soon enough..
     234             :        */
     235             :     }
     236             :   else
     237             :     {
     238             :       /*
     239             :        * G or SG query. we'll need to save the sources quered
     240             :        */
     241           9 :       igmp_key_t key = {
     242             :         .ip4 = args->query[0].group_address,
     243             :       };
     244             :       ip46_address_t *srcs;
     245             :       igmp_timer_id_t tid;
     246             :       igmp_group_t *group;
     247             : 
     248           9 :       group = igmp_group_lookup (config, &key);
     249             : 
     250             :       /*
     251             :        * If there is no group config, no worries, we can ignore this
     252             :        * query. If the group state does come soon, we'll send a
     253             :        * state-change report at that time.
     254             :        */
     255           9 :       if (!group)
     256           0 :         return;
     257             : 
     258           9 :       srcs = igmp_query_mk_source_list (args->query);
     259           9 :       tid = group->timers[IGMP_GROUP_TIMER_QUERY_REPLY];
     260             : 
     261           9 :       IGMP_DBG ("...group-query-rx: %U for (%U, %U)",
     262             :                 format_vnet_sw_if_index_name,
     263             :                 vnet_get_main (), args->sw_if_index,
     264             :                 format_igmp_src_addr_list, srcs, format_igmp_key, &key);
     265             : 
     266             : 
     267           9 :       if (IGMP_TIMER_ID_INVALID != tid)
     268             :         {
     269             :           /*
     270             :            * There is a timer already running, merge the sources list
     271             :            */
     272             :           ip46_address_t *current, *s;
     273             : 
     274           2 :           current = igmp_timer_get_data (tid);
     275             : 
     276           4 :           vec_foreach (s, srcs)
     277             :           {
     278           4 :             if (~0 == vec_search_with_function (current, s,
     279             :                                                 ip46_address_is_equal))
     280             :               {
     281           2 :                 vec_add1 (current, *s);
     282             :               }
     283             :           }
     284             : 
     285           2 :           igmp_timer_set_data (tid, current);
     286             :         }
     287             :       else
     288             :         {
     289             :           /*
     290             :            * schedule a new G-specific query
     291             :            */
     292           7 :           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
     293             : 
     294           7 :           IGMP_DBG ("...group-query-rx: schedule:%f", delay);
     295             : 
     296           7 :           group->timers[IGMP_GROUP_TIMER_QUERY_REPLY] =
     297           7 :             igmp_timer_schedule (delay,
     298             :                                  igmp_group_index (group),
     299             :                                  igmp_send_group_report_v3, srcs);
     300             :         }
     301             :     }
     302             : }
     303             : 
     304             : 
     305             : /*
     306             :  * fd.io coding-style-patch-verification: ON
     307             :  *
     308             :  * Local Variables:
     309             :  * eval: (c-set-style "gnu")
     310             :  * End:
     311             :  */

Generated by: LCOV version 1.14