Trivial: Clean up some typos.
[vpp.git] / src / plugins / igmp / igmp_query.c
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 igmp_get_random_resp_delay (const igmp_header_t * header)
23 {
24   u32 seed;
25
26   seed = vlib_time_now (vlib_get_main ());
27
28   return ((random_f64 (&seed) * igmp_header_get_max_resp_time (header)));
29
30 }
31
32 static ip46_address_t *
33 igmp_query_mk_source_list (const igmp_membership_query_v3_t * q)
34 {
35   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   n = clib_net_to_host_u16 (q->n_src_addresses);
44
45   if (0 == n)
46     return (NULL);
47
48   vec_validate (srcs, n - 1);
49   s = q->src_addresses;
50
51   for (ii = 0; ii < n; ii++)
52     {
53       srcs[ii].ip4 = *s;
54       s++;
55     }
56
57   return (srcs);
58 }
59
60 static void
61 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   im = &igmp_main;
70   srcs = data;
71   group = pool_elt_at_index (im->groups, obj);
72   config = pool_elt_at_index (im->configs, group->config);
73
74   igmp_pkt_build_report_init (&br, config->sw_if_index);
75   ASSERT (group->timers[IGMP_GROUP_TIMER_QUERY_REPLY] !=
76           IGMP_TIMER_ID_INVALID);
77
78   IGMP_DBG ("send-group-report: %U",
79             format_vnet_sw_if_index_name,
80             vnet_get_main (), config->sw_if_index);
81
82   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       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       intersect = igmp_group_new_intersect_present (group,
100                                                     IGMP_FILTER_MODE_INCLUDE,
101                                                     srcs);
102
103       if (vec_len (intersect))
104         {
105           igmp_pkt_report_v3_add_report (&br,
106                                          group->key,
107                                          intersect,
108                                          IGMP_MEMBERSHIP_GROUP_mode_is_include);
109           vec_free (intersect);
110         }
111     }
112
113   igmp_pkt_report_v3_send (&br);
114
115   igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_QUERY_REPLY]);
116   vec_free (srcs);
117 }
118
119 static igmp_membership_group_v3_type_t
120 igmp_filter_mode_to_report_type (igmp_filter_mode_t mode)
121 {
122   switch (mode)
123     {
124     case IGMP_FILTER_MODE_INCLUDE:
125       return (IGMP_MEMBERSHIP_GROUP_mode_is_include);
126     case IGMP_FILTER_MODE_EXCLUDE:
127       return (IGMP_MEMBERSHIP_GROUP_mode_is_exclude);
128     }
129
130   return (IGMP_MEMBERSHIP_GROUP_mode_is_include);
131 }
132
133 /**
134  * Send igmp membership general report.
135  */
136 static void
137 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   im = &igmp_main;
145   config = pool_elt_at_index (im->configs, obj);
146
147   ASSERT (config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] !=
148           IGMP_TIMER_ID_INVALID);
149
150   igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT]);
151
152   IGMP_DBG ("send-general-report: %U",
153             format_vnet_sw_if_index_name,
154             vnet_get_main (), config->sw_if_index);
155
156   igmp_pkt_build_report_init (&br, config->sw_if_index);
157
158   /* *INDENT-OFF* */
159   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   igmp_pkt_report_v3_send (&br);
168 }
169
170 /**
171  * Called from the main thread on reception of a Query message
172  */
173 void
174 igmp_handle_query (const igmp_query_args_t * args)
175 {
176   igmp_config_t *config;
177
178   config = igmp_config_lookup (args->sw_if_index);
179
180   if (!config)
181     /*
182      * no IGMP config on the interface. quit
183      */
184     return;
185
186   if (IGMP_MODE_ROUTER == config->mode)
187     {
188       ASSERT (0);
189       // code here for querier election */
190     }
191
192   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   if (igmp_membership_query_v3_is_geeral (args->query))
207     {
208       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       if (IGMP_TIMER_ID_INVALID ==
215           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT])
216         {
217           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
218
219           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           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] =
227             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       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       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       if (!group)
256         return;
257
258       srcs = igmp_query_mk_source_list (args->query);
259       tid = group->timers[IGMP_GROUP_TIMER_QUERY_REPLY];
260
261       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       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           current = igmp_timer_get_data (tid);
275
276           vec_foreach (s, srcs)
277           {
278             if (~0 == vec_search_with_function (current, s,
279                                                 ip46_address_is_equal))
280               {
281                 vec_add1 (current, *s);
282               }
283           }
284
285           igmp_timer_set_data (tid, current);
286         }
287       else
288         {
289           /*
290            * schedule a new G-specific query
291            */
292           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
293
294           IGMP_DBG ("...group-query-rx: schedule:%f", delay);
295
296           group->timers[IGMP_GROUP_TIMER_QUERY_REPLY] =
297             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  */