2 *------------------------------------------------------------------
3 * Copyright (c) 2017 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 <vlib/vlib.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vpp/app/version.h>
21 #include <vnet/ip/ip.h>
22 #include <vlib/unix/unix.h>
23 #include <vnet/adj/adj_mcast.h>
25 #include <igmp/igmp.h>
26 #include <igmp/error.h>
41 IGMP_INPUT_NEXT_PARSE_QUERY,
42 IGMP_INPUT_NEXT_PARSE_REPORT,
48 IGMP_PARSE_QUERY_NEXT_DROP,
49 IGMP_PARSE_QUERY_N_NEXT,
50 } igmp_parse_query_next_t;
54 IGMP_PARSE_REPORT_NEXT_DROP,
55 IGMP_PARSE_REPORT_N_NEXT,
56 } igmp_parse_report_next_t;
58 char *igmp_error_strings[] = {
59 #define _(sym,string) string,
73 format_igmp_input_trace (u8 * s, va_list * va)
75 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
76 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
77 igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
80 format (s, "sw_if_index %u next-index %u", t->sw_if_index, t->next_index);
82 format (s, "\n%U", format_igmp_header, t->packet_data,
83 sizeof (t->packet_data));
88 format_igmp_parse_report_trace (u8 * s, va_list * va)
90 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
91 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
92 igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
95 format (s, "sw_if_index %u next-index %u", t->sw_if_index, t->next_index);
97 format (s, "\n%U", format_igmp_report_v3, t->packet_data,
98 sizeof (t->packet_data));
103 format_igmp_parse_query_trace (u8 * s, va_list * va)
105 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
106 CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
107 igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
110 format (s, "sw_if_index %u next-input %u", t->sw_if_index, t->next_index);
112 format (s, "\n%U", format_igmp_query_v3, t->packet_data,
113 sizeof (t->packet_data));
118 igmp_input (vlib_main_t * vm, vlib_node_runtime_t * node,
119 vlib_frame_t * frame)
121 IGMP_DBG ("IGMP_INPUT");
122 u32 n_left_from, *from, *to_next;
123 igmp_parse_query_next_t next_index;
124 vlib_node_runtime_t *error_node =
125 vlib_node_get_runtime (vm, igmp_input_node.index);
130 error = IGMP_ERROR_NONE;
132 from = vlib_frame_vector_args (frame);
133 n_left_from = frame->n_vectors;
134 next_index = node->cached_next_index;
136 while (n_left_from > 0)
140 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
142 while (n_left_from > 0 && n_left_to_next > 0)
147 next = IGMP_INPUT_NEXT_DROP;
156 b = vlib_get_buffer (vm, bi);
157 ip = vlib_buffer_get_current (b);
159 if (ip->protocol != 2)
161 error = IGMP_ERROR_INVALID_PROTOCOL;
162 next = IGMP_INPUT_NEXT_DROP;
166 vlib_buffer_advance (b, ip4_header_bytes (ip));
168 igmp_header_t *igmp = vlib_buffer_get_current (b);
170 u16 checksum = igmp->checksum;
172 sum = ip_incremental_checksum (0, igmp,
173 clib_net_to_host_u16 (ip->length) -
174 ip4_header_bytes (ip));
175 igmp->checksum = checksum;
176 csum = ~ip_csum_fold (sum);
177 if (checksum != csum)
179 error = IGMP_ERROR_BAD_CHECKSUM;
180 next = IGMP_INPUT_NEXT_DROP;
184 /* TODO: IGMPv2 and IGMPv1 */
187 case IGMP_TYPE_membership_query:
188 next = IGMP_INPUT_NEXT_PARSE_QUERY;
190 case IGMP_TYPE_membership_report_v3:
191 next = IGMP_INPUT_NEXT_PARSE_REPORT;
194 error = IGMP_ERROR_UNKNOWN_TYPE;
195 next = IGMP_INPUT_NEXT_DROP;
199 b->error = error_node->errors[error];
201 if (node->flags & VLIB_NODE_FLAG_TRACE)
203 igmp_input_trace_t *tr;
204 tr = vlib_add_trace (vm, node, b, sizeof (*tr));
205 tr->next_index = next;
206 tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
207 clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
208 sizeof (tr->packet_data));
211 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
212 n_left_to_next, bi, next);
214 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
217 return frame->n_vectors;
221 VLIB_REGISTER_NODE (igmp_input_node) =
223 .function = igmp_input,
224 .name = "igmp-input",
225 .vector_size = sizeof (u32),
227 .format_buffer = format_igmp_header,
228 .format_trace = format_igmp_input_trace,
230 .n_errors = IGMP_N_ERROR,
231 .error_strings = igmp_error_strings,
233 .n_next_nodes = IGMP_INPUT_N_NEXT,
235 [IGMP_INPUT_NEXT_DROP] = "error-drop",
236 [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
237 [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
243 igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
244 vlib_frame_t * frame)
246 IGMP_DBG ("IGMP_PARSE_QUERY");
248 u32 n_left_from, *from, *to_next;
249 igmp_parse_query_next_t next_index;
250 igmp_main_t *im = &igmp_main;
251 igmp_config_t *config;
253 from = vlib_frame_vector_args (frame);
254 n_left_from = frame->n_vectors;
255 next_index = node->cached_next_index;
257 while (n_left_from > 0)
261 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
263 while (n_left_from > 0 && n_left_to_next > 0)
266 u32 sw_if_index, bi, next;
267 next = IGMP_PARSE_QUERY_NEXT_DROP;
276 b = vlib_get_buffer (vm, bi);
277 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
279 igmp_membership_query_v3_t *igmp = vlib_buffer_get_current (b);
280 ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
282 /* if group address is zero, this is a general query */
283 if (igmp->dst.as_u32 == 0)
285 config = igmp_config_lookup (im, sw_if_index);
288 IGMP_DBG ("No config on interface %u", sw_if_index);
294 * TODO: divide to multipe reports in random time range [now, max resp time]
296 u32 seed = vlib_time_now (vm);
297 f64 next_resp_time = random_f64 (&seed) *
298 (f64) (igmp->header.code / 10) + vlib_time_now (vm);
299 config->flags |= IGMP_CONFIG_FLAG_CAN_SEND_REPORT;
300 igmp_create_int_timer (next_resp_time, sw_if_index,
302 vlib_process_signal_event (vm,
303 igmp_timer_process_node.index,
304 IGMP_PROCESS_EVENT_UPDATE_TIMER,
309 if (node->flags & VLIB_NODE_FLAG_TRACE)
311 igmp_input_trace_t *tr;
312 tr = vlib_add_trace (vm, node, b, sizeof (*tr));
313 tr->next_index = next;
314 tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
315 clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
316 sizeof (tr->packet_data));
318 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
319 n_left_to_next, bi, next);
321 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
324 return frame->n_vectors;
328 VLIB_REGISTER_NODE (igmp_parse_query_node) =
330 .function = igmp_parse_query,
331 .name = "igmp-parse-query",
332 .vector_size = sizeof (u32),
334 .format_buffer = format_igmp_query_v3,
335 .format_trace = format_igmp_parse_query_trace,
337 .n_errors = IGMP_N_ERROR,
338 .error_strings = igmp_error_strings,
340 .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
342 [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
348 igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
349 vlib_frame_t * frame)
351 IGMP_DBG ("IGMP_PARSE_REPORT");
353 igmp_main_t *im = &igmp_main;
354 u32 n_left_from, *from, *to_next;
355 igmp_input_next_t next_index;
356 igmp_config_t *config;
359 igmp_membership_group_v3_t *igmp_group;
360 ip4_address_t *src_addr;
363 memset (&gkey, 0, sizeof (igmp_key_t));
364 memset (&skey, 0, sizeof (igmp_key_t));
365 ip46_address_t saddr;
366 memset (&saddr, 0, sizeof (ip46_address_t));
367 ip46_address_t gaddr;
368 memset (&gaddr, 0, sizeof (ip46_address_t));
370 vlib_node_runtime_t *error_node =
371 vlib_node_get_runtime (vm, igmp_input_node.index);
374 from = vlib_frame_vector_args (frame);
375 n_left_from = frame->n_vectors;
376 next_index = node->cached_next_index;
378 while (n_left_from > 0)
382 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
384 while (n_left_from > 0 && n_left_to_next > 0)
387 u32 sw_if_index, bi, next;
388 next = IGMP_PARSE_REPORT_NEXT_DROP;
397 b = vlib_get_buffer (vm, bi);
399 error = IGMP_ERROR_NONE;
400 b->error = error_node->errors[error];
402 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
404 igmp_membership_report_v3_t *igmp = vlib_buffer_get_current (b);
405 ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
406 len = sizeof (igmp_membership_report_v3_t);
408 /* if interface (S,G)s were configured by CLI/API goto next frame */
409 config = igmp_config_lookup (im, sw_if_index);
412 config->flags |= IGMP_CONFIG_FLAG_QUERY_RESP_RECVED;
413 if (config->cli_api_configured)
415 IGMP_DBG ("Interface %u has (S,G)s configured by CLI/API",
417 error = IGMP_ERROR_CLI_API_CONFIG;
418 b->error = error_node->errors[error];
422 IGMP_DBG ("interface %u", sw_if_index);
424 for (i = 0; i < clib_net_to_host_u16 (igmp->n_groups); i++)
426 igmp_group = group_ptr (igmp, len);
427 src_addr = igmp_group->src_addresses;
428 if (igmp_group->type ==
429 IGMP_MEMBERSHIP_GROUP_mode_is_filter_include)
431 ip46_address_set_ip4 ((ip46_address_t *) & gkey.data,
432 &igmp_group->dst_address);
435 IGMP_MEMBERSHIP_GROUP_mode_is_filter_include;
437 group = igmp_group_lookup (config, &gkey);
442 clib_net_to_host_u16 (igmp_group->n_src_addresses);
445 /* update (S,G) expiration timer */
446 ip46_address_set_ip4 ((ip46_address_t *) &
447 skey.data, src_addr);
448 src = igmp_src_lookup (group, &skey);
451 vlib_time_now (vm) + IGMP_SRC_TIMER;
456 else if (igmp_group->type ==
457 IGMP_MEMBERSHIP_GROUP_mode_is_filter_exclude)
460 j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
463 /* nothing for now... */
467 else if (igmp_group->type ==
468 IGMP_MEMBERSHIP_GROUP_change_to_filter_include)
471 j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
474 /* add new (S,G) to interface */
475 saddr.ip4 = *src_addr;
476 gaddr.ip4 = igmp_group->dst_address;
477 igmp_listen (vm, 1, sw_if_index, saddr, gaddr, 0);
481 else if (igmp_group->type ==
482 IGMP_MEMBERSHIP_GROUP_change_to_filter_exclude)
485 j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
488 /* remove (S,G) from interface */
489 saddr.ip4 = *src_addr;
490 gaddr.ip4 = igmp_group->dst_address;
491 igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0);
495 else if (igmp_group->type ==
496 IGMP_MEMBERSHIP_GROUP_allow_new_sources)
499 j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
502 /* nothing for now... */
506 else if (igmp_group->type ==
507 IGMP_MEMBERSHIP_GROUP_block_old_sources)
510 j < clib_net_to_host_u16 (igmp_group->n_src_addresses);
513 /* remove (S,G) from interface */
514 saddr.ip4 = *src_addr;
515 gaddr.ip4 = igmp_group->dst_address;
516 igmp_listen (vm, 0, sw_if_index, saddr, gaddr, 0);
521 * Unrecognized Record Type values MUST be silently ignored.
524 /* move ptr to next Group Record */
526 sizeof (igmp_membership_group_v3_t) +
527 (sizeof (ip4_address_t) * j);
530 if (node->flags & VLIB_NODE_FLAG_TRACE)
532 igmp_input_trace_t *tr;
533 tr = vlib_add_trace (vm, node, b, sizeof (*tr));
534 tr->next_index = next;
535 tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
536 clib_memcpy (tr->packet_data, vlib_buffer_get_current (b),
537 sizeof (tr->packet_data));
539 vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
540 n_left_to_next, bi, next);
542 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
545 return frame->n_vectors;
549 VLIB_REGISTER_NODE (igmp_parse_report_node) =
551 .function = igmp_parse_report,
552 .name = "igmp-parse-report",
553 .vector_size = sizeof (u32),
555 .format_buffer = format_igmp_report_v3,
556 .format_trace = format_igmp_parse_report_trace,
558 .n_errors = IGMP_N_ERROR,
559 .error_strings = igmp_error_strings,
561 .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
563 [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
569 * fd.io coding-style-patch-verification: ON
572 * eval: (c-set-style "gnu")