vrrp: fix vrrp_garp_or_na_send()'s memory leak
[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   FOR_EACH_GROUP (group, config,
159     ({
160       igmp_pkt_report_v3_add_group
161         (&br, group,
162          igmp_filter_mode_to_report_type(group->router_filter_mode));
163     }));
164
165   igmp_pkt_report_v3_send (&br);
166 }
167
168 /**
169  * Called from the main thread on reception of a Query message
170  */
171 void
172 igmp_handle_query (const igmp_query_args_t * args)
173 {
174   igmp_config_t *config;
175
176   config = igmp_config_lookup (args->sw_if_index);
177
178   if (!config)
179     /*
180      * no IGMP config on the interface. quit
181      */
182     return;
183
184   if (IGMP_MODE_ROUTER == config->mode)
185     {
186       ASSERT (0);
187       // code here for querier election */
188     }
189
190   IGMP_DBG ("query-rx: %U", format_vnet_sw_if_index_name,
191             vnet_get_main (), args->sw_if_index);
192
193
194   /*
195      Section 5.2
196      "When a system receives a Query, it does not respond immediately.
197      Instead, it delays its response by a random amount of time, bounded
198      by the Max Resp Time value derived from the Max Resp Code in the
199      received Query message.  A system may receive a variety of Queries on
200      different interfaces and of different kinds (e.g., General Queries,
201      Group-Specific Queries, and Group-and-Source-Specific Queries), each
202      of which may require its own delayed response.
203    */
204   if (igmp_membership_query_v3_is_general (args->query))
205     {
206       IGMP_DBG ("...general-query-rx: %U", format_vnet_sw_if_index_name,
207                 vnet_get_main (), args->sw_if_index);
208
209       /*
210        * A general query has no info that needs saving from the response
211        */
212       if (IGMP_TIMER_ID_INVALID ==
213           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT])
214         {
215           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
216
217           IGMP_DBG ("...general-query-rx: %U schedule for %f",
218                     format_vnet_sw_if_index_name, vnet_get_main (),
219                     args->sw_if_index, delay);
220
221           /*
222            * no currently running timer, schedule a new one
223            */
224           config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] =
225             igmp_timer_schedule (delay,
226                                  igmp_config_index (config),
227                                  igmp_send_general_report_v3, NULL);
228         }
229       /*
230        * else
231        *  don't reschedule timers, we'll reply soon enough..
232        */
233     }
234   else
235     {
236       /*
237        * G or SG query. we'll need to save the sources quered
238        */
239       igmp_key_t key = {
240         .ip4 = args->query[0].group_address,
241       };
242       ip46_address_t *srcs;
243       igmp_timer_id_t tid;
244       igmp_group_t *group;
245
246       group = igmp_group_lookup (config, &key);
247
248       /*
249        * If there is no group config, no worries, we can ignore this
250        * query. If the group state does come soon, we'll send a
251        * state-change report at that time.
252        */
253       if (!group)
254         return;
255
256       srcs = igmp_query_mk_source_list (args->query);
257       tid = group->timers[IGMP_GROUP_TIMER_QUERY_REPLY];
258
259       IGMP_DBG ("...group-query-rx: %U for (%U, %U)",
260                 format_vnet_sw_if_index_name,
261                 vnet_get_main (), args->sw_if_index,
262                 format_igmp_src_addr_list, srcs, format_igmp_key, &key);
263
264
265       if (IGMP_TIMER_ID_INVALID != tid)
266         {
267           /*
268            * There is a timer already running, merge the sources list
269            */
270           ip46_address_t *current, *s;
271
272           current = igmp_timer_get_data (tid);
273
274           vec_foreach (s, srcs)
275           {
276             if (~0 == vec_search_with_function (current, s,
277                                                 ip46_address_is_equal))
278               {
279                 vec_add1 (current, *s);
280               }
281           }
282
283           igmp_timer_set_data (tid, current);
284         }
285       else
286         {
287           /*
288            * schedule a new G-specific query
289            */
290           f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
291
292           IGMP_DBG ("...group-query-rx: schedule:%f", delay);
293
294           group->timers[IGMP_GROUP_TIMER_QUERY_REPLY] =
295             igmp_timer_schedule (delay,
296                                  igmp_group_index (group),
297                                  igmp_send_group_report_v3, srcs);
298         }
299     }
300 }
301
302
303 /*
304  * fd.io coding-style-patch-verification: ON
305  *
306  * Local Variables:
307  * eval: (c-set-style "gnu")
308  * End:
309  */