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 : */
|