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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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 *------------------------------------------------------------------
18 #include <igmp/igmp_query.h>
19 #include <igmp/igmp_pkt.h>
22 igmp_get_random_resp_delay (const igmp_header_t * header)
26 seed = vlib_time_now (vlib_get_main ());
28 return ((random_f64 (&seed) * igmp_header_get_max_resp_time (header)));
32 static ip46_address_t *
33 igmp_query_mk_source_list (const igmp_membership_query_v3_t * q)
35 ip46_address_t *srcs = NULL;
36 const ip4_address_t *s;
40 * we validated this packet when we accepted it in the DP, so
41 * this number is safe to use
43 n = clib_net_to_host_u16 (q->n_src_addresses);
48 vec_validate (srcs, n - 1);
51 for (ii = 0; ii < n; ii++)
61 igmp_send_group_report_v3 (u32 obj, void *data)
63 igmp_pkt_build_report_t br;
64 igmp_config_t *config;
71 group = pool_elt_at_index (im->groups, obj);
72 config = pool_elt_at_index (im->configs, group->config);
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);
78 IGMP_DBG ("send-group-report: %U",
79 format_vnet_sw_if_index_name,
80 vnet_get_main (), config->sw_if_index);
85 * there were no sources specified, so this is a group-specific query.
86 * We should respond with all our sources
88 igmp_pkt_report_v3_add_group (&br, group,
89 IGMP_MEMBERSHIP_GROUP_mode_is_include);
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.
97 ip46_address_t *intersect;
99 intersect = igmp_group_new_intersect_present (group,
100 IGMP_FILTER_MODE_INCLUDE,
103 if (vec_len (intersect))
105 igmp_pkt_report_v3_add_report (&br,
108 IGMP_MEMBERSHIP_GROUP_mode_is_include);
109 vec_free (intersect);
113 igmp_pkt_report_v3_send (&br);
115 igmp_timer_retire (&group->timers[IGMP_GROUP_TIMER_QUERY_REPLY]);
119 static igmp_membership_group_v3_type_t
120 igmp_filter_mode_to_report_type (igmp_filter_mode_t mode)
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);
130 return (IGMP_MEMBERSHIP_GROUP_mode_is_include);
134 * Send igmp membership general report.
137 igmp_send_general_report_v3 (u32 obj, void *data)
139 igmp_pkt_build_report_t br;
140 igmp_config_t *config;
145 config = pool_elt_at_index (im->configs, obj);
147 ASSERT (config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT] !=
148 IGMP_TIMER_ID_INVALID);
150 igmp_timer_retire (&config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT]);
152 IGMP_DBG ("send-general-report: %U",
153 format_vnet_sw_if_index_name,
154 vnet_get_main (), config->sw_if_index);
156 igmp_pkt_build_report_init (&br, config->sw_if_index);
159 FOR_EACH_GROUP (group, config,
161 igmp_pkt_report_v3_add_group
163 igmp_filter_mode_to_report_type(group->router_filter_mode));
167 igmp_pkt_report_v3_send (&br);
171 * Called from the main thread on reception of a Query message
174 igmp_handle_query (const igmp_query_args_t * args)
176 igmp_config_t *config;
178 config = igmp_config_lookup (args->sw_if_index);
182 * no IGMP config on the interface. quit
186 if (IGMP_MODE_ROUTER == config->mode)
189 // code here for querier election */
192 IGMP_DBG ("query-rx: %U", format_vnet_sw_if_index_name,
193 vnet_get_main (), args->sw_if_index);
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.
206 if (igmp_membership_query_v3_is_geeral (args->query))
208 IGMP_DBG ("...general-query-rx: %U", format_vnet_sw_if_index_name,
209 vnet_get_main (), args->sw_if_index);
212 * A general query has no info that needs saving from the response
214 if (IGMP_TIMER_ID_INVALID ==
215 config->timers[IGMP_CONFIG_TIMER_GENERAL_REPORT])
217 f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
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);
224 * no currently running timer, schedule a new one
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);
233 * don't reschedule timers, we'll reply soon enough..
239 * G or SG query. we'll need to save the sources quered
242 .ip4 = args->query[0].group_address,
244 ip46_address_t *srcs;
248 group = igmp_group_lookup (config, &key);
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.
258 srcs = igmp_query_mk_source_list (args->query);
259 tid = group->timers[IGMP_GROUP_TIMER_QUERY_REPLY];
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);
267 if (IGMP_TIMER_ID_INVALID != tid)
270 * There is a timer already running, merge the sources list
272 ip46_address_t *current, *s;
274 current = igmp_timer_get_data (tid);
276 vec_foreach (s, srcs)
278 if (~0 == vec_search_with_function (current, s,
279 ip46_address_is_equal))
281 vec_add1 (current, *s);
285 igmp_timer_set_data (tid, current);
290 * schedule a new G-specific query
292 f64 delay = igmp_get_random_resp_delay (&args->query[0].header);
294 IGMP_DBG ("...group-query-rx: schedule:%f", delay);
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);
306 * fd.io coding-style-patch-verification: ON
309 * eval: (c-set-style "gnu")