]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv3.c
bgpd: Allow self next-hop if `bgp allow-martian-nexthop` is enabled
[mirror_frr.git] / pimd / pim_igmpv3.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for Quagga
4 * Copyright (C) 2008 Everton da Silva Marques
5 */
6
7 #include <zebra.h>
8 #include "log.h"
9 #include "memory.h"
10 #include "if.h"
11 #include "lib_errors.h"
12
13 #include "pimd.h"
14 #include "pim_instance.h"
15 #include "pim_iface.h"
16 #include "pim_igmp.h"
17 #include "pim_igmpv3.h"
18 #include "pim_str.h"
19 #include "pim_util.h"
20 #include "pim_time.h"
21 #include "pim_zebra.h"
22 #include "pim_oil.h"
23 #include "pim_ssm.h"
24
25 static void group_retransmit_timer_on(struct gm_group *group);
26 static long igmp_group_timer_remain_msec(struct gm_group *group);
27 static long igmp_source_timer_remain_msec(struct gm_source *source);
28 static void group_query_send(struct gm_group *group);
29 static void source_query_send_by_flag(struct gm_group *group,
30 int num_sources_tosend);
31
32 static void on_trace(const char *label, struct interface *ifp,
33 struct in_addr from, struct in_addr group_addr,
34 int num_sources, struct in_addr *sources)
35 {
36 if (PIM_DEBUG_GM_TRACE) {
37 char from_str[INET_ADDRSTRLEN];
38 char group_str[INET_ADDRSTRLEN];
39
40 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
41 pim_inet4_dump("<group?>", group_addr, group_str,
42 sizeof(group_str));
43
44 zlog_debug("%s: from %s on %s: group=%s sources=%d", label,
45 from_str, ifp->name, group_str, num_sources);
46 }
47 }
48
49 static inline long igmp_gmi_msec(struct gm_group *group)
50 {
51 struct pim_interface *pim_ifp = group->interface->info;
52 struct gm_sock *igmp;
53 struct listnode *sock_node;
54
55 long qrv = 0, qqi = 0;
56
57 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
58 qrv = MAX(qrv, igmp->querier_robustness_variable);
59 qqi = MAX(qqi, igmp->querier_query_interval);
60 }
61 return PIM_IGMP_GMI_MSEC(qrv, qqi,
62 pim_ifp->gm_query_max_response_time_dsec);
63 }
64
65 void igmp_group_reset_gmi(struct gm_group *group)
66 {
67 long group_membership_interval_msec;
68 struct interface *ifp;
69
70 ifp = group->interface;
71
72 /*
73 RFC 3376: 8.4. Group Membership Interval
74
75 The Group Membership Interval is the amount of time that must pass
76 before a multicast router decides there are no more members of a
77 group or a particular source on a network.
78
79 This value MUST be ((the Robustness Variable) times (the Query
80 Interval)) plus (one Query Response Interval).
81
82 group_membership_interval_msec = querier_robustness_variable *
83 (1000 * querier_query_interval) +
84 100 * query_response_interval_dsec;
85 */
86 group_membership_interval_msec = igmp_gmi_msec(group);
87
88 if (PIM_DEBUG_GM_TRACE) {
89 char group_str[INET_ADDRSTRLEN];
90 pim_inet4_dump("<group?>", group->group_addr, group_str,
91 sizeof(group_str));
92 zlog_debug(
93 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
94 group_str, group_membership_interval_msec / 1000,
95 group_membership_interval_msec % 1000, ifp->name);
96 }
97
98 /*
99 RFC 3376: 6.2.2. Definition of Group Timers
100
101 The group timer is only used when a group is in EXCLUDE mode and
102 it represents the time for the *filter-mode* of the group to
103 expire and switch to INCLUDE mode.
104 */
105 assert(group->group_filtermode_isexcl);
106
107 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
108 }
109
110 static void igmp_source_timer(struct thread *t)
111 {
112 struct gm_source *source;
113 struct gm_group *group;
114
115 source = THREAD_ARG(t);
116
117 group = source->source_group;
118
119 if (PIM_DEBUG_GM_TRACE) {
120 char group_str[INET_ADDRSTRLEN];
121 char source_str[INET_ADDRSTRLEN];
122 pim_inet4_dump("<group?>", group->group_addr, group_str,
123 sizeof(group_str));
124 pim_inet4_dump("<source?>", source->source_addr, source_str,
125 sizeof(source_str));
126 zlog_debug(
127 "%s: Source timer expired for group %s source %s on %s",
128 __func__, group_str, source_str,
129 group->interface->name);
130 }
131
132 /*
133 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
134
135 Group
136 Filter-Mode Source Timer Value Action
137 ----------- ------------------ ------
138 INCLUDE TIMER == 0 Suggest to stop forwarding
139 traffic from source and
140 remove source record. If
141 there are no more source
142 records for the group, delete
143 group record.
144
145 EXCLUDE TIMER == 0 Suggest to not forward
146 traffic from source
147 (DO NOT remove record)
148
149 Source timer switched from (T > 0) to (T == 0): disable forwarding.
150 */
151
152 if (group->group_filtermode_isexcl) {
153 /* EXCLUDE mode */
154
155 igmp_source_forward_stop(source);
156 } else {
157 /* INCLUDE mode */
158
159 /* igmp_source_delete() will stop forwarding source */
160 igmp_source_delete(source);
161
162 /*
163 If there are no more source records for the group, delete
164 group
165 record.
166 */
167 if (!listcount(group->group_source_list)) {
168 igmp_group_delete_empty_include(group);
169 }
170 }
171 }
172
173 static void source_timer_off(struct gm_group *group, struct gm_source *source)
174 {
175 if (!source->t_source_timer)
176 return;
177
178 if (PIM_DEBUG_GM_TRACE) {
179 char group_str[INET_ADDRSTRLEN];
180 char source_str[INET_ADDRSTRLEN];
181 pim_inet4_dump("<group?>", group->group_addr, group_str,
182 sizeof(group_str));
183 pim_inet4_dump("<source?>", source->source_addr, source_str,
184 sizeof(source_str));
185 zlog_debug(
186 "Cancelling TIMER event for group %s source %s on %s",
187 group_str, source_str, group->interface->name);
188 }
189
190 THREAD_OFF(source->t_source_timer);
191 }
192
193 static void igmp_source_timer_on(struct gm_group *group,
194 struct gm_source *source, long interval_msec)
195 {
196 source_timer_off(group, source);
197 struct pim_interface *pim_ifp = group->interface->info;
198
199 if (PIM_DEBUG_GM_EVENTS) {
200 char group_str[INET_ADDRSTRLEN];
201 char source_str[INET_ADDRSTRLEN];
202 pim_inet4_dump("<group?>", group->group_addr, group_str,
203 sizeof(group_str));
204 pim_inet4_dump("<source?>", source->source_addr, source_str,
205 sizeof(source_str));
206 zlog_debug(
207 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
208 interval_msec / 1000, interval_msec % 1000, group_str,
209 source_str, group->interface->name);
210 }
211
212 thread_add_timer_msec(router->master, igmp_source_timer, source,
213 interval_msec, &source->t_source_timer);
214
215 /*
216 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
217
218 Source timer switched from (T == 0) to (T > 0): enable forwarding.
219 */
220 igmp_source_forward_start(pim_ifp->pim, source);
221 }
222
223 void igmp_source_reset_gmi(struct gm_group *group, struct gm_source *source)
224 {
225 long group_membership_interval_msec;
226 struct interface *ifp;
227
228 ifp = group->interface;
229
230 group_membership_interval_msec = igmp_gmi_msec(group);
231
232 if (PIM_DEBUG_GM_TRACE) {
233 char group_str[INET_ADDRSTRLEN];
234 char source_str[INET_ADDRSTRLEN];
235
236 pim_inet4_dump("<group?>", group->group_addr, group_str,
237 sizeof(group_str));
238 pim_inet4_dump("<source?>", source->source_addr, source_str,
239 sizeof(source_str));
240
241 zlog_debug(
242 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
243 source_str, group_membership_interval_msec / 1000,
244 group_membership_interval_msec % 1000, group_str,
245 ifp->name);
246 }
247
248 igmp_source_timer_on(group, source, group_membership_interval_msec);
249 }
250
251 static void source_mark_delete_flag(struct gm_group *group)
252 {
253 struct listnode *src_node;
254 struct gm_source *src;
255
256 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
257 IGMP_SOURCE_DO_DELETE(src->source_flags);
258 }
259 }
260
261 static void source_mark_send_flag(struct gm_group *group)
262 {
263 struct listnode *src_node;
264 struct gm_source *src;
265
266 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
267 IGMP_SOURCE_DO_SEND(src->source_flags);
268 }
269 }
270
271 static int source_mark_send_flag_by_timer(struct gm_group *group)
272 {
273 struct listnode *src_node;
274 struct gm_source *src;
275 int num_marked_sources = 0;
276
277 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
278 /* Is source timer running? */
279 if (src->t_source_timer) {
280 IGMP_SOURCE_DO_SEND(src->source_flags);
281 ++num_marked_sources;
282 } else {
283 IGMP_SOURCE_DONT_SEND(src->source_flags);
284 }
285 }
286
287 return num_marked_sources;
288 }
289
290 static void source_clear_send_flag(struct list *source_list)
291 {
292 struct listnode *src_node;
293 struct gm_source *src;
294
295 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
296 IGMP_SOURCE_DONT_SEND(src->source_flags);
297 }
298 }
299
300 /*
301 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
302 */
303 static void group_exclude_fwd_anysrc_ifempty(struct gm_group *group)
304 {
305 struct pim_interface *pim_ifp = group->interface->info;
306
307 assert(group->group_filtermode_isexcl);
308
309 if (listcount(group->group_source_list) < 1) {
310 igmp_anysource_forward_start(pim_ifp->pim, group);
311 }
312 }
313
314 void igmp_source_free(struct gm_source *source)
315 {
316 /* make sure there is no source timer running */
317 assert(!source->t_source_timer);
318
319 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
320 }
321
322 static void source_channel_oil_detach(struct gm_source *source)
323 {
324 if (source->source_channel_oil) {
325 pim_channel_oil_del(source->source_channel_oil, __func__);
326 source->source_channel_oil = NULL;
327 }
328 }
329
330 /*
331 igmp_source_delete: stop forwarding, and delete the source
332 igmp_source_forward_stop: stop forwarding, but keep the source
333 */
334 void igmp_source_delete(struct gm_source *source)
335 {
336 struct gm_group *group;
337 struct in_addr src;
338
339 group = source->source_group;
340
341 if (PIM_DEBUG_GM_TRACE) {
342 char group_str[INET_ADDRSTRLEN];
343 char source_str[INET_ADDRSTRLEN];
344 pim_inet4_dump("<group?>", group->group_addr, group_str,
345 sizeof(group_str));
346 pim_inet4_dump("<source?>", source->source_addr, source_str,
347 sizeof(source_str));
348 zlog_debug(
349 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
350 source_str, group_str, group->interface->name,
351 source->source_channel_oil
352 ? source->source_channel_oil->oil_ref_count
353 : 0);
354 }
355
356 source_timer_off(group, source);
357 igmp_source_forward_stop(source);
358
359 /* sanity check that forwarding has been disabled */
360 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
361 char group_str[INET_ADDRSTRLEN];
362 char source_str[INET_ADDRSTRLEN];
363 pim_inet4_dump("<group?>", group->group_addr, group_str,
364 sizeof(group_str));
365 pim_inet4_dump("<source?>", source->source_addr, source_str,
366 sizeof(source_str));
367 zlog_warn(
368 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
369 __func__, source_str, group_str,
370 group->interface->name);
371 /* warning only */
372 }
373
374 source_channel_oil_detach(source);
375
376 /*
377 notice that listnode_delete() can't be moved
378 into igmp_source_free() because the later is
379 called by list_delete_all_node()
380 */
381 listnode_delete(group->group_source_list, source);
382
383 src.s_addr = source->source_addr.s_addr;
384 igmp_source_free(source);
385
386 /* Group source list is empty and current source is * then
387 *,G group going away so do not trigger start */
388 if (group->group_filtermode_isexcl
389 && (listcount(group->group_source_list) != 0)
390 && src.s_addr != INADDR_ANY) {
391 group_exclude_fwd_anysrc_ifempty(group);
392 }
393 }
394
395 static void source_delete_by_flag(struct list *source_list)
396 {
397 struct listnode *src_node;
398 struct listnode *src_nextnode;
399 struct gm_source *src;
400
401 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
402 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
403 igmp_source_delete(src);
404 }
405
406 void igmp_source_delete_expired(struct list *source_list)
407 {
408 struct listnode *src_node;
409 struct listnode *src_nextnode;
410 struct gm_source *src;
411
412 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
413 if (!src->t_source_timer)
414 igmp_source_delete(src);
415 }
416
417 struct gm_source *igmp_find_source_by_addr(struct gm_group *group,
418 struct in_addr src_addr)
419 {
420 struct listnode *src_node;
421 struct gm_source *src;
422
423 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
424 if (src_addr.s_addr == src->source_addr.s_addr)
425 return src;
426
427 return 0;
428 }
429
430 struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
431 struct in_addr src_addr, bool *new)
432 {
433 struct gm_source *src;
434
435 if (new)
436 *new = false;
437
438 src = igmp_find_source_by_addr(group, src_addr);
439 if (src)
440 return src;
441
442 if (PIM_DEBUG_GM_TRACE) {
443 char group_str[INET_ADDRSTRLEN];
444 char source_str[INET_ADDRSTRLEN];
445 pim_inet4_dump("<group?>", group->group_addr, group_str,
446 sizeof(group_str));
447 pim_inet4_dump("<source?>", src_addr, source_str,
448 sizeof(source_str));
449 zlog_debug(
450 "Creating new IGMP source %s for group %s on interface %s",
451 source_str, group_str, group->interface->name);
452 }
453
454 src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
455
456 if (new)
457 *new = true;
458
459 src->t_source_timer = NULL;
460 src->source_group = group; /* back pointer */
461 src->source_addr = src_addr;
462 src->source_creation = pim_time_monotonic_sec();
463 src->source_flags = 0;
464 src->source_query_retransmit_count = 0;
465 src->source_channel_oil = NULL;
466
467 listnode_add(group->group_source_list, src);
468
469 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
470 igmp_anysource_forward_stop(group);
471 return src;
472 }
473
474 static void allow(struct gm_sock *igmp, struct in_addr from,
475 struct in_addr group_addr, int num_sources,
476 struct in_addr *sources)
477 {
478 struct gm_source *source;
479 struct gm_group *group;
480 int i;
481
482 if (num_sources == 0) {
483 /*
484 RFC 3376: 3.1. Socket-State
485 If the requested filter mode is INCLUDE *and* the requested
486 source list is empty, then the entry corresponding to the
487 requested interface and multicast address is deleted if
488 present. If no such entry is present, the request is ignored.
489 So, deleting the group present.
490 */
491 group = find_group_by_addr(igmp, group_addr);
492 if (!group) {
493 return;
494 }
495 if (group->group_filtermode_isexcl) {
496 if (listcount(group->group_source_list) == 1) {
497 struct in_addr star = {.s_addr = INADDR_ANY};
498
499 source = igmp_find_source_by_addr(group, star);
500 if (source)
501 igmp_source_reset_gmi(group, source);
502 }
503 } else {
504 igmp_group_delete(group);
505 }
506
507 return;
508 }
509
510 /* non-existent group is created as INCLUDE {empty} */
511 group = igmp_add_group_by_addr(igmp, group_addr);
512 if (!group) {
513 return;
514 }
515
516 /* scan received sources */
517 for (i = 0; i < num_sources; ++i) {
518 struct in_addr *src_addr;
519
520 src_addr = sources + i;
521
522 source = igmp_get_source_by_addr(group, *src_addr, NULL);
523 if (!source)
524 continue;
525
526 /*
527 RFC 3376: 6.4.1. Reception of Current-State Records
528
529 When receiving IS_IN reports for groups in EXCLUDE mode is
530 sources should be moved from set with (timers = 0) to set with
531 (timers > 0).
532
533 igmp_source_reset_gmi() below, resetting the source timers to
534 GMI, accomplishes this.
535 */
536 igmp_source_reset_gmi(group, source);
537
538 } /* scan received sources */
539 }
540
541 void igmpv3_report_isin(struct gm_sock *igmp, struct in_addr from,
542 struct in_addr group_addr, int num_sources,
543 struct in_addr *sources)
544 {
545 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
546 sources);
547
548 allow(igmp, from, group_addr, num_sources, sources);
549 }
550
551 static void isex_excl(struct gm_group *group, int num_sources,
552 struct in_addr *sources)
553 {
554 struct gm_source *source;
555 int i;
556
557 /* EXCLUDE mode */
558 assert(group->group_filtermode_isexcl);
559
560 /* E.1: set deletion flag for known sources (X,Y) */
561 source_mark_delete_flag(group);
562
563 /* scan received sources (A) */
564 for (i = 0; i < num_sources; ++i) {
565 struct in_addr *src_addr;
566 bool new;
567
568 src_addr = sources + i;
569
570 /* E.2: lookup reported source from (A) in (X,Y) */
571 source = igmp_get_source_by_addr(group, *src_addr, &new);
572 if (!source)
573 continue;
574
575 if (!new) {
576 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
577 IGMP_SOURCE_DONT_DELETE(source->source_flags);
578 } else {
579 /* E.4: if not found, create source with timer=GMI:
580 * (A-X-Y) */
581 assert(!source->t_source_timer); /* timer == 0 */
582 igmp_source_reset_gmi(group, source);
583 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
584 }
585
586 } /* scan received sources */
587
588 /*
589 * If we are in isexcl mode and num_sources == 0
590 * than that means we have a *,g entry that
591 * needs to be handled
592 */
593 if (group->group_filtermode_isexcl && num_sources == 0) {
594 struct in_addr star = {.s_addr = INADDR_ANY};
595 source = igmp_find_source_by_addr(group, star);
596 if (source) {
597 IGMP_SOURCE_DONT_DELETE(source->source_flags);
598 igmp_source_reset_gmi(group, source);
599 }
600 }
601
602 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
603 source_delete_by_flag(group->group_source_list);
604 }
605
606 static void isex_incl(struct gm_group *group, int num_sources,
607 struct in_addr *sources)
608 {
609 int i;
610
611 /* INCLUDE mode */
612 assert(!group->group_filtermode_isexcl);
613
614 /* I.1: set deletion flag for known sources (A) */
615 source_mark_delete_flag(group);
616
617 /* scan received sources (B) */
618 for (i = 0; i < num_sources; ++i) {
619 struct gm_source *source;
620 struct in_addr *src_addr;
621 bool new;
622
623 src_addr = sources + i;
624
625 /* I.2: lookup reported source (B) */
626 source = igmp_get_source_by_addr(group, *src_addr, &new);
627 if (!source)
628 continue;
629
630 if (!new) {
631 /* I.3: if found, clear deletion flag (A*B) */
632 IGMP_SOURCE_DONT_DELETE(source->source_flags);
633 } else {
634 /* I.4: if not found, create source with timer=0 (B-A)
635 */
636 assert(!source->t_source_timer); /* (B-A) timer=0 */
637 }
638
639 } /* scan received sources */
640
641 /* I.5: delete all sources marked with deletion flag (A-B) */
642 source_delete_by_flag(group->group_source_list);
643
644 group->group_filtermode_isexcl = 1; /* boolean=true */
645
646 assert(group->group_filtermode_isexcl);
647
648 group_exclude_fwd_anysrc_ifempty(group);
649 }
650
651 void igmpv3_report_isex(struct gm_sock *igmp, struct in_addr from,
652 struct in_addr group_addr, int num_sources,
653 struct in_addr *sources, int from_igmp_v2_report)
654 {
655 struct interface *ifp = igmp->interface;
656 struct gm_group *group;
657
658 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
659
660 if (pim_is_group_filtered(ifp->info, &group_addr))
661 return;
662
663 /* non-existent group is created as INCLUDE {empty} */
664 group = igmp_add_group_by_addr(igmp, group_addr);
665 if (!group) {
666 return;
667 }
668
669 /* So we can display how we learned the group in our show command output
670 */
671 if (from_igmp_v2_report)
672 group->igmp_version = 2;
673
674 if (group->group_filtermode_isexcl) {
675 /* EXCLUDE mode */
676 isex_excl(group, num_sources, sources);
677 } else {
678 /* INCLUDE mode */
679 isex_incl(group, num_sources, sources);
680 assert(group->group_filtermode_isexcl);
681 }
682
683 assert(group->group_filtermode_isexcl);
684
685 igmp_group_reset_gmi(group);
686 }
687
688 static void toin_incl(struct gm_group *group, int num_sources,
689 struct in_addr *sources)
690 {
691 int num_sources_tosend = listcount(group->group_source_list);
692 int i;
693
694 /* Set SEND flag for all known sources (A) */
695 source_mark_send_flag(group);
696
697 /* Scan received sources (B) */
698 for (i = 0; i < num_sources; ++i) {
699 struct gm_source *source;
700 struct in_addr *src_addr;
701 bool new;
702
703 src_addr = sources + i;
704
705 /* Lookup reported source (B) */
706 source = igmp_get_source_by_addr(group, *src_addr, &new);
707 if (!source)
708 continue;
709
710 if (!new) {
711 /* If found, clear SEND flag (A*B) */
712 IGMP_SOURCE_DONT_SEND(source->source_flags);
713 --num_sources_tosend;
714 }
715
716 /* (B)=GMI */
717 igmp_source_reset_gmi(group, source);
718 }
719
720 /* Send sources marked with SEND flag: Q(G,A-B) */
721 if (num_sources_tosend > 0) {
722 source_query_send_by_flag(group, num_sources_tosend);
723 }
724 }
725
726 static void toin_excl(struct gm_group *group, int num_sources,
727 struct in_addr *sources)
728 {
729 int num_sources_tosend;
730 int i;
731
732 /* Set SEND flag for X (sources with timer > 0) */
733 num_sources_tosend = source_mark_send_flag_by_timer(group);
734
735 /* Scan received sources (A) */
736 for (i = 0; i < num_sources; ++i) {
737 struct gm_source *source;
738 struct in_addr *src_addr;
739 bool new;
740
741 src_addr = sources + i;
742
743 /* Lookup reported source (A) */
744 source = igmp_get_source_by_addr(group, *src_addr, &new);
745 if (!source)
746 continue;
747
748 if (source->t_source_timer) {
749 /* If found and timer running, clear SEND flag
750 * (X*A) */
751 IGMP_SOURCE_DONT_SEND(source->source_flags);
752 --num_sources_tosend;
753 }
754
755 /* (A)=GMI */
756 igmp_source_reset_gmi(group, source);
757 }
758
759 /* Send sources marked with SEND flag: Q(G,X-A) */
760 if (num_sources_tosend > 0) {
761 source_query_send_by_flag(group, num_sources_tosend);
762 }
763
764 /* Send Q(G) */
765 group_query_send(group);
766 }
767
768 void igmpv3_report_toin(struct gm_sock *igmp, struct in_addr from,
769 struct in_addr group_addr, int num_sources,
770 struct in_addr *sources)
771 {
772 struct interface *ifp = igmp->interface;
773 struct gm_group *group;
774
775 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
776
777 /*
778 * If the requested filter mode is INCLUDE *and* the requested source
779 * list is empty, then the entry corresponding to the requested
780 * interface and multicast address is deleted if present. If no such
781 * entry is present, the request is ignored.
782 */
783 if (num_sources) {
784 /* non-existent group is created as INCLUDE {empty} */
785 group = igmp_add_group_by_addr(igmp, group_addr);
786 if (!group) {
787 return;
788 }
789 } else {
790 group = find_group_by_addr(igmp, group_addr);
791 if (!group)
792 return;
793 }
794
795 if (group->group_filtermode_isexcl) {
796 /* EXCLUDE mode */
797 toin_excl(group, num_sources, sources);
798 } else {
799 /* INCLUDE mode */
800 toin_incl(group, num_sources, sources);
801 }
802 }
803
804 static void toex_incl(struct gm_group *group, int num_sources,
805 struct in_addr *sources)
806 {
807 int num_sources_tosend = 0;
808 int i;
809
810 assert(!group->group_filtermode_isexcl);
811
812 /* Set DELETE flag for all known sources (A) */
813 source_mark_delete_flag(group);
814
815 /* Clear off SEND flag from all known sources (A) */
816 source_clear_send_flag(group->group_source_list);
817
818 /* Scan received sources (B) */
819 for (i = 0; i < num_sources; ++i) {
820 struct gm_source *source;
821 struct in_addr *src_addr;
822 bool new;
823
824 src_addr = sources + i;
825
826 /* Lookup reported source (B) */
827 source = igmp_get_source_by_addr(group, *src_addr, &new);
828 if (!new) {
829 /* If found, clear deletion flag: (A*B) */
830 IGMP_SOURCE_DONT_DELETE(source->source_flags);
831 /* and set SEND flag (A*B) */
832 IGMP_SOURCE_DO_SEND(source->source_flags);
833 ++num_sources_tosend;
834 }
835
836 } /* Scan received sources (B) */
837
838 group->group_filtermode_isexcl = 1; /* boolean=true */
839
840 /* Delete all sources marked with DELETE flag (A-B) */
841 source_delete_by_flag(group->group_source_list);
842
843 /* Send sources marked with SEND flag: Q(G,A*B) */
844 if (num_sources_tosend > 0) {
845 source_query_send_by_flag(group, num_sources_tosend);
846 }
847
848 assert(group->group_filtermode_isexcl);
849
850 group_exclude_fwd_anysrc_ifempty(group);
851 }
852
853 static void toex_excl(struct gm_group *group, int num_sources,
854 struct in_addr *sources)
855 {
856 int num_sources_tosend = 0;
857 int i;
858
859 /* set DELETE flag for all known sources (X,Y) */
860 source_mark_delete_flag(group);
861
862 /* clear off SEND flag from all known sources (X,Y) */
863 source_clear_send_flag(group->group_source_list);
864
865 if (num_sources == 0) {
866 struct gm_source *source;
867 struct in_addr any = {.s_addr = INADDR_ANY};
868
869 source = igmp_find_source_by_addr(group, any);
870 if (source)
871 IGMP_SOURCE_DONT_DELETE(source->source_flags);
872 }
873
874 /* scan received sources (A) */
875 for (i = 0; i < num_sources; ++i) {
876 struct gm_source *source;
877 struct in_addr *src_addr;
878 bool new;
879
880 src_addr = sources + i;
881
882 /* lookup reported source (A) in known sources (X,Y) */
883 source = igmp_get_source_by_addr(group, *src_addr, &new);
884 if (!source)
885 continue;
886
887 if (!new) {
888 /* if found, clear off DELETE flag from reported source
889 * (A) */
890 IGMP_SOURCE_DONT_DELETE(source->source_flags);
891 } else {
892 /* if not found, create source with Group Timer:
893 * (A-X-Y)=Group Timer */
894 long group_timer_msec;
895
896 assert(!source->t_source_timer); /* timer == 0 */
897 group_timer_msec = igmp_group_timer_remain_msec(group);
898 igmp_source_timer_on(group, source, group_timer_msec);
899 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
900
901 /* make sure source is created with DELETE flag unset */
902 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
903 }
904
905 /* make sure reported source has DELETE flag unset */
906 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
907
908 if (source->t_source_timer) {
909 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
910 IGMP_SOURCE_DO_SEND(source->source_flags);
911 ++num_sources_tosend;
912 }
913
914 } /* scan received sources (A) */
915
916 /*
917 delete all sources marked with DELETE flag:
918 Delete (X-A)
919 Delete (Y-A)
920 */
921 source_delete_by_flag(group->group_source_list);
922
923 /* send sources marked with SEND flag: Q(G,A-Y) */
924 if (num_sources_tosend > 0) {
925 source_query_send_by_flag(group, num_sources_tosend);
926 }
927 }
928
929 void igmpv3_report_toex(struct gm_sock *igmp, struct in_addr from,
930 struct in_addr group_addr, int num_sources,
931 struct in_addr *sources)
932 {
933 struct interface *ifp = igmp->interface;
934 struct gm_group *group;
935
936 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
937
938 /* non-existent group is created as INCLUDE {empty} */
939 group = igmp_add_group_by_addr(igmp, group_addr);
940 if (!group) {
941 return;
942 }
943
944 if (group->group_filtermode_isexcl) {
945 /* EXCLUDE mode */
946 toex_excl(group, num_sources, sources);
947 } else {
948 /* INCLUDE mode */
949 toex_incl(group, num_sources, sources);
950 assert(group->group_filtermode_isexcl);
951 }
952 assert(group->group_filtermode_isexcl);
953
954 /* Group Timer=GMI */
955 igmp_group_reset_gmi(group);
956 }
957
958 void igmpv3_report_allow(struct gm_sock *igmp, struct in_addr from,
959 struct in_addr group_addr, int num_sources,
960 struct in_addr *sources)
961 {
962 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
963 sources);
964
965 allow(igmp, from, group_addr, num_sources, sources);
966 }
967
968 static void igmp_send_query_group(struct gm_group *group, char *query_buf,
969 size_t query_buf_size, int num_sources,
970 int s_flag)
971 {
972 struct interface *ifp = group->interface;
973 struct pim_interface *pim_ifp = ifp->info;
974 struct gm_sock *igmp;
975 struct listnode *sock_node;
976
977 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
978 igmp_send_query(
979 pim_ifp->igmp_version, group, query_buf, query_buf_size,
980 num_sources, group->group_addr, group->group_addr,
981 pim_ifp->gm_specific_query_max_response_time_dsec,
982 s_flag, igmp);
983 }
984 }
985
986 /*
987 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
988
989 When transmitting a group specific query, if the group timer is
990 larger than LMQT, the "Suppress Router-Side Processing" bit is set
991 in the query message.
992 */
993 static void group_retransmit_group(struct gm_group *group)
994 {
995 struct pim_interface *pim_ifp;
996 long lmqc; /* Last Member Query Count */
997 long lmqi_msec; /* Last Member Query Interval */
998 long lmqt_msec; /* Last Member Query Time */
999 int s_flag;
1000 int query_buf_size;
1001
1002 pim_ifp = group->interface->info;
1003
1004 if (pim_ifp->igmp_version == 3) {
1005 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1006 } else {
1007 query_buf_size = IGMP_V12_MSG_SIZE;
1008 }
1009
1010 char query_buf[query_buf_size];
1011
1012 lmqc = pim_ifp->gm_last_member_query_count;
1013 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1014 lmqt_msec = lmqc * lmqi_msec;
1015
1016 /*
1017 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1018
1019 When transmitting a group specific query, if the group timer is
1020 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1021 in the query message.
1022 */
1023 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1024
1025 if (PIM_DEBUG_GM_TRACE) {
1026 char group_str[INET_ADDRSTRLEN];
1027 pim_inet4_dump("<group?>", group->group_addr, group_str,
1028 sizeof(group_str));
1029 zlog_debug(
1030 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1031 group_str, group->interface->name, s_flag,
1032 group->group_specific_query_retransmit_count);
1033 }
1034
1035 /*
1036 RFC3376: 4.1.12. IP Destination Addresses for Queries
1037
1038 Group-Specific and Group-and-Source-Specific Queries are sent with
1039 an IP destination address equal to the multicast address of
1040 interest.
1041 */
1042
1043 igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag);
1044 }
1045
1046 /*
1047 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1048
1049 When building a group and source specific query for a group G, two
1050 separate query messages are sent for the group. The first one has
1051 the "Suppress Router-Side Processing" bit set and contains all the
1052 sources with retransmission state and timers greater than LMQT. The
1053 second has the "Suppress Router-Side Processing" bit clear and
1054 contains all the sources with retransmission state and timers lower
1055 or equal to LMQT. If either of the two calculated messages does not
1056 contain any sources, then its transmission is suppressed.
1057 */
1058 static int group_retransmit_sources(struct gm_group *group,
1059 int send_with_sflag_set)
1060 {
1061 struct pim_interface *pim_ifp;
1062 long lmqc; /* Last Member Query Count */
1063 long lmqi_msec; /* Last Member Query Interval */
1064 long lmqt_msec; /* Last Member Query Time */
1065 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1066 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1067 int query_buf1_max_sources;
1068 int query_buf2_max_sources;
1069 struct in_addr *source_addr1;
1070 struct in_addr *source_addr2;
1071 int num_sources_tosend1;
1072 int num_sources_tosend2;
1073 struct listnode *src_node;
1074 struct gm_source *src;
1075 int num_retransmit_sources_left = 0;
1076
1077 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1078 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1079
1080 pim_ifp = group->interface->info;
1081
1082 lmqc = pim_ifp->gm_last_member_query_count;
1083 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1084 lmqt_msec = lmqc * lmqi_msec;
1085
1086 /* Scan all group sources */
1087 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1088
1089 /* Source has retransmission state? */
1090 if (src->source_query_retransmit_count < 1)
1091 continue;
1092
1093 if (--src->source_query_retransmit_count > 0) {
1094 ++num_retransmit_sources_left;
1095 }
1096
1097 /* Copy source address into appropriate query buffer */
1098 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1099 *source_addr1 = src->source_addr;
1100 ++source_addr1;
1101 } else {
1102 *source_addr2 = src->source_addr;
1103 ++source_addr2;
1104 }
1105 }
1106
1107 num_sources_tosend1 =
1108 source_addr1
1109 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1110 num_sources_tosend2 =
1111 source_addr2
1112 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1113
1114 if (PIM_DEBUG_GM_TRACE) {
1115 char group_str[INET_ADDRSTRLEN];
1116 pim_inet4_dump("<group?>", group->group_addr, group_str,
1117 sizeof(group_str));
1118 zlog_debug(
1119 "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
1120 group_str, group->interface->name, num_sources_tosend1,
1121 num_sources_tosend2, send_with_sflag_set,
1122 num_retransmit_sources_left);
1123 }
1124
1125 if (num_sources_tosend1 > 0) {
1126 /*
1127 Send group-and-source-specific query with s_flag set and all
1128 sources with timers greater than LMQT.
1129 */
1130
1131 if (send_with_sflag_set) {
1132
1133 query_buf1_max_sources =
1134 (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET)
1135 >> 2;
1136 if (num_sources_tosend1 > query_buf1_max_sources) {
1137 char group_str[INET_ADDRSTRLEN];
1138 pim_inet4_dump("<group?>", group->group_addr,
1139 group_str, sizeof(group_str));
1140 zlog_warn(
1141 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1142 __func__, group_str,
1143 group->interface->name,
1144 num_sources_tosend1, sizeof(query_buf1),
1145 query_buf1_max_sources);
1146 } else {
1147 /*
1148 RFC3376: 4.1.12. IP Destination Addresses for
1149 Queries
1150
1151 Group-Specific and Group-and-Source-Specific
1152 Queries are sent with
1153 an IP destination address equal to the
1154 multicast address of
1155 interest.
1156 */
1157
1158 igmp_send_query_group(
1159 group, query_buf1, sizeof(query_buf1),
1160 num_sources_tosend1, 1 /* s_flag */);
1161 }
1162
1163 } /* send_with_sflag_set */
1164 }
1165
1166 if (num_sources_tosend2 > 0) {
1167 /*
1168 Send group-and-source-specific query with s_flag clear and all
1169 sources with timers lower or equal to LMQT.
1170 */
1171
1172 query_buf2_max_sources =
1173 (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1174 if (num_sources_tosend2 > query_buf2_max_sources) {
1175 char group_str[INET_ADDRSTRLEN];
1176 pim_inet4_dump("<group?>", group->group_addr, group_str,
1177 sizeof(group_str));
1178 zlog_warn(
1179 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1180 __func__, group_str, group->interface->name,
1181 num_sources_tosend2, sizeof(query_buf2),
1182 query_buf2_max_sources);
1183 } else {
1184 /*
1185 RFC3376: 4.1.12. IP Destination Addresses for Queries
1186
1187 Group-Specific and Group-and-Source-Specific Queries
1188 are sent with
1189 an IP destination address equal to the multicast
1190 address of
1191 interest.
1192 */
1193
1194 igmp_send_query_group(
1195 group, query_buf2, sizeof(query_buf2),
1196 num_sources_tosend2, 0 /* s_flag */);
1197 }
1198 }
1199
1200 return num_retransmit_sources_left;
1201 }
1202
1203 static void igmp_group_retransmit(struct thread *t)
1204 {
1205 struct gm_group *group;
1206 int num_retransmit_sources_left;
1207 int send_with_sflag_set; /* boolean */
1208
1209 group = THREAD_ARG(t);
1210
1211 if (PIM_DEBUG_GM_TRACE) {
1212 char group_str[INET_ADDRSTRLEN];
1213 pim_inet4_dump("<group?>", group->group_addr, group_str,
1214 sizeof(group_str));
1215 zlog_debug("group_retransmit_timer: group %s on %s", group_str,
1216 group->interface->name);
1217 }
1218
1219 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1220 if (group->group_specific_query_retransmit_count > 0) {
1221
1222 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1223 group_retransmit_group(group);
1224 --group->group_specific_query_retransmit_count;
1225
1226 /*
1227 RFC3376: 6.6.3.2
1228 If a group specific query is scheduled to be transmitted at
1229 the
1230 same time as a group and source specific query for the same
1231 group,
1232 then transmission of the group and source specific message
1233 with the
1234 "Suppress Router-Side Processing" bit set may be suppressed.
1235 */
1236 send_with_sflag_set = 0; /* boolean=false */
1237 } else {
1238 send_with_sflag_set = 1; /* boolean=true */
1239 }
1240
1241 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1242 num_retransmit_sources_left =
1243 group_retransmit_sources(group, send_with_sflag_set);
1244
1245 /*
1246 Keep group retransmit timer running if there is any retransmit
1247 counter pending
1248 */
1249 if ((num_retransmit_sources_left > 0)
1250 || (group->group_specific_query_retransmit_count > 0)) {
1251 group_retransmit_timer_on(group);
1252 }
1253 }
1254
1255 /*
1256 group_retransmit_timer_on:
1257 if group retransmit timer isn't running, starts it;
1258 otherwise, do nothing
1259 */
1260 static void group_retransmit_timer_on(struct gm_group *group)
1261 {
1262 struct pim_interface *pim_ifp;
1263 long lmqi_msec; /* Last Member Query Interval */
1264
1265 /* if group retransmit timer is running, do nothing */
1266 if (group->t_group_query_retransmit_timer) {
1267 return;
1268 }
1269
1270 pim_ifp = group->interface->info;
1271
1272 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1273
1274 if (PIM_DEBUG_GM_TRACE) {
1275 char group_str[INET_ADDRSTRLEN];
1276 pim_inet4_dump("<group?>", group->group_addr, group_str,
1277 sizeof(group_str));
1278 zlog_debug(
1279 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1280 lmqi_msec / 1000, lmqi_msec % 1000, group_str,
1281 group->interface->name);
1282 }
1283
1284 thread_add_timer_msec(router->master, igmp_group_retransmit, group,
1285 lmqi_msec,
1286 &group->t_group_query_retransmit_timer);
1287 }
1288
1289 static long igmp_group_timer_remain_msec(struct gm_group *group)
1290 {
1291 return pim_time_timer_remain_msec(group->t_group_timer);
1292 }
1293
1294 static long igmp_source_timer_remain_msec(struct gm_source *source)
1295 {
1296 return pim_time_timer_remain_msec(source->t_source_timer);
1297 }
1298
1299 /*
1300 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1301 */
1302 static void group_query_send(struct gm_group *group)
1303 {
1304 struct pim_interface *pim_ifp;
1305 long lmqc; /* Last Member Query Count */
1306
1307 pim_ifp = group->interface->info;
1308 lmqc = pim_ifp->gm_last_member_query_count;
1309
1310 /* lower group timer to lmqt */
1311 igmp_group_timer_lower_to_lmqt(group);
1312
1313 /* reset retransmission counter */
1314 group->group_specific_query_retransmit_count = lmqc;
1315
1316 /* immediately send group specific query (decrease retransmit counter by
1317 * 1)*/
1318 group_retransmit_group(group);
1319
1320 /* make sure group retransmit timer is running */
1321 group_retransmit_timer_on(group);
1322 }
1323
1324 /*
1325 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1326 */
1327 static void source_query_send_by_flag(struct gm_group *group,
1328 int num_sources_tosend)
1329 {
1330 struct pim_interface *pim_ifp;
1331 struct listnode *src_node;
1332 struct gm_source *src;
1333 long lmqc; /* Last Member Query Count */
1334 long lmqi_msec; /* Last Member Query Interval */
1335 long lmqt_msec; /* Last Member Query Time */
1336
1337 assert(num_sources_tosend > 0);
1338
1339 pim_ifp = group->interface->info;
1340
1341 lmqc = pim_ifp->gm_last_member_query_count;
1342 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1343 lmqt_msec = lmqc * lmqi_msec;
1344
1345 /*
1346 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1347 Queries
1348
1349 (...) for each of the sources in X of group G, with source timer
1350 larger
1351 than LMQT:
1352 o Set number of retransmissions for each source to [Last Member
1353 Query Count].
1354 o Lower source timer to LMQT.
1355 */
1356 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1357 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1358 /* source "src" in X of group G */
1359 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1360 src->source_query_retransmit_count = lmqc;
1361 igmp_source_timer_lower_to_lmqt(src);
1362 }
1363 }
1364 }
1365
1366 /* send group-and-source specific queries */
1367 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1368
1369 /* make sure group retransmit timer is running */
1370 group_retransmit_timer_on(group);
1371 }
1372
1373 static void block_excl(struct gm_group *group, int num_sources,
1374 struct in_addr *sources)
1375 {
1376 int num_sources_tosend = 0;
1377 int i;
1378
1379 /* 1. clear off SEND flag from all known sources (X,Y) */
1380 source_clear_send_flag(group->group_source_list);
1381
1382 /* 2. scan received sources (A) */
1383 for (i = 0; i < num_sources; ++i) {
1384 struct gm_source *source;
1385 struct in_addr *src_addr;
1386 bool new;
1387
1388 src_addr = sources + i;
1389
1390 /* lookup reported source (A) in known sources (X,Y) */
1391 source = igmp_get_source_by_addr(group, *src_addr, &new);
1392 if (!source)
1393 continue;
1394
1395 if (new) {
1396 /* 3: if not found, create source with Group Timer:
1397 * (A-X-Y)=Group Timer */
1398 long group_timer_msec;
1399
1400 assert(!source->t_source_timer); /* timer == 0 */
1401 group_timer_msec = igmp_group_timer_remain_msec(group);
1402 igmp_source_timer_on(group, source, group_timer_msec);
1403 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1404 }
1405
1406 if (source->t_source_timer) {
1407 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1408 IGMP_SOURCE_DO_SEND(source->source_flags);
1409 ++num_sources_tosend;
1410 }
1411 }
1412
1413 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1414 if (num_sources_tosend > 0) {
1415 source_query_send_by_flag(group, num_sources_tosend);
1416 }
1417 }
1418
1419 static void block_incl(struct gm_group *group, int num_sources,
1420 struct in_addr *sources)
1421 {
1422 int num_sources_tosend = 0;
1423 int i;
1424
1425 /* 1. clear off SEND flag from all known sources (B) */
1426 source_clear_send_flag(group->group_source_list);
1427
1428 /* 2. scan received sources (A) */
1429 for (i = 0; i < num_sources; ++i) {
1430 struct gm_source *source;
1431 struct in_addr *src_addr;
1432
1433 src_addr = sources + i;
1434
1435 /* lookup reported source (A) in known sources (B) */
1436 source = igmp_find_source_by_addr(group, *src_addr);
1437 if (source) {
1438 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1439 IGMP_SOURCE_DO_SEND(source->source_flags);
1440 ++num_sources_tosend;
1441 }
1442 }
1443
1444 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1445 if (num_sources_tosend > 0) {
1446 source_query_send_by_flag(group, num_sources_tosend);
1447 }
1448 }
1449
1450 void igmpv3_report_block(struct gm_sock *igmp, struct in_addr from,
1451 struct in_addr group_addr, int num_sources,
1452 struct in_addr *sources)
1453 {
1454 struct interface *ifp = igmp->interface;
1455 struct gm_group *group;
1456
1457 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
1458
1459 /* non-existent group is created as INCLUDE {empty} */
1460 group = igmp_add_group_by_addr(igmp, group_addr);
1461 if (!group) {
1462 return;
1463 }
1464
1465 if (group->group_filtermode_isexcl) {
1466 /* EXCLUDE mode */
1467 block_excl(group, num_sources, sources);
1468 } else {
1469 /* INCLUDE mode */
1470 block_incl(group, num_sources, sources);
1471 }
1472 }
1473
1474 void igmp_group_timer_lower_to_lmqt(struct gm_group *group)
1475 {
1476 struct interface *ifp;
1477 struct pim_interface *pim_ifp;
1478 char *ifname;
1479 int lmqi_dsec; /* Last Member Query Interval */
1480 int lmqc; /* Last Member Query Count */
1481 int lmqt_msec; /* Last Member Query Time */
1482
1483 /*
1484 RFC 3376: 6.2.2. Definition of Group Timers
1485
1486 The group timer is only used when a group is in EXCLUDE mode and
1487 it represents the time for the *filter-mode* of the group to
1488 expire and switch to INCLUDE mode.
1489 */
1490 if (!group->group_filtermode_isexcl) {
1491 return;
1492 }
1493
1494 ifp = group->interface;
1495 pim_ifp = ifp->info;
1496 ifname = ifp->name;
1497
1498 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1499 lmqc = pim_ifp->gm_last_member_query_count;
1500 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1501 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1502
1503 if (PIM_DEBUG_GM_TRACE) {
1504 char group_str[INET_ADDRSTRLEN];
1505 pim_inet4_dump("<group?>", group->group_addr, group_str,
1506 sizeof(group_str));
1507 zlog_debug(
1508 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1509 __func__, group_str, ifname, lmqc, lmqi_dsec,
1510 lmqt_msec);
1511 }
1512
1513 assert(group->group_filtermode_isexcl);
1514
1515 igmp_group_timer_on(group, lmqt_msec, ifname);
1516 }
1517
1518 void igmp_source_timer_lower_to_lmqt(struct gm_source *source)
1519 {
1520 struct gm_group *group;
1521 struct interface *ifp;
1522 struct pim_interface *pim_ifp;
1523 char *ifname;
1524 int lmqi_dsec; /* Last Member Query Interval */
1525 int lmqc; /* Last Member Query Count */
1526 int lmqt_msec; /* Last Member Query Time */
1527
1528 group = source->source_group;
1529 ifp = group->interface;
1530 pim_ifp = ifp->info;
1531 ifname = ifp->name;
1532
1533 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1534 lmqc = pim_ifp->gm_last_member_query_count;
1535 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1536 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1537
1538 if (PIM_DEBUG_GM_TRACE) {
1539 char group_str[INET_ADDRSTRLEN];
1540 char source_str[INET_ADDRSTRLEN];
1541 pim_inet4_dump("<group?>", group->group_addr, group_str,
1542 sizeof(group_str));
1543 pim_inet4_dump("<source?>", source->source_addr, source_str,
1544 sizeof(source_str));
1545 zlog_debug(
1546 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1547 __func__, group_str, source_str, ifname, lmqc,
1548 lmqi_dsec, lmqt_msec);
1549 }
1550
1551 igmp_source_timer_on(group, source, lmqt_msec);
1552 }
1553
1554 void igmp_v3_send_query(struct gm_group *group, int fd, const char *ifname,
1555 char *query_buf, int query_buf_size, int num_sources,
1556 struct in_addr dst_addr, struct in_addr group_addr,
1557 int query_max_response_time_dsec, uint8_t s_flag,
1558 uint8_t querier_robustness_variable,
1559 uint16_t querier_query_interval)
1560 {
1561 ssize_t msg_size;
1562 uint8_t max_resp_code;
1563 uint8_t qqic;
1564 ssize_t sent;
1565 struct sockaddr_in to;
1566 socklen_t tolen;
1567 uint16_t checksum;
1568
1569 assert(num_sources >= 0);
1570
1571 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1572 if (msg_size > query_buf_size) {
1573 flog_err(
1574 EC_LIB_DEVELOPMENT,
1575 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1576 __FILE__, __func__, msg_size, query_buf_size);
1577 return;
1578 }
1579
1580 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1581 assert((s_flag == 0) || (s_flag == 1));
1582
1583 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1584 qqic = igmp_msg_encode16to8(querier_query_interval);
1585
1586 /*
1587 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1588
1589 If non-zero, the QRV field contains the [Robustness Variable]
1590 value used by the querier, i.e., the sender of the Query. If the
1591 querier's [Robustness Variable] exceeds 7, the maximum value of
1592 the QRV field, the QRV is set to zero.
1593 */
1594 if (querier_robustness_variable > 7) {
1595 querier_robustness_variable = 0;
1596 }
1597
1598 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1599 query_buf[1] = max_resp_code;
1600 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1601 0; /* for computing checksum */
1602 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1603
1604 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1605 query_buf[9] = qqic;
1606 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1607 htons(num_sources);
1608
1609 checksum = in_cksum(query_buf, msg_size);
1610 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1611
1612 if (PIM_DEBUG_GM_PACKETS) {
1613 char dst_str[INET_ADDRSTRLEN];
1614 char group_str[INET_ADDRSTRLEN];
1615 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1616 pim_inet4_dump("<group?>", group_addr, group_str,
1617 sizeof(group_str));
1618 zlog_debug(
1619 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1620 dst_str, ifname, group_str, num_sources, msg_size,
1621 s_flag, querier_robustness_variable,
1622 querier_query_interval, qqic);
1623 }
1624
1625 memset(&to, 0, sizeof(to));
1626 to.sin_family = AF_INET;
1627 to.sin_addr = dst_addr;
1628 tolen = sizeof(to);
1629
1630 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1631 (struct sockaddr *)&to, tolen);
1632 if (sent != (ssize_t)msg_size) {
1633 char dst_str[INET_ADDRSTRLEN];
1634 char group_str[INET_ADDRSTRLEN];
1635 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1636 pim_inet4_dump("<group?>", group_addr, group_str,
1637 sizeof(group_str));
1638 if (sent < 0) {
1639 zlog_warn(
1640 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1641 dst_str, ifname, group_str, msg_size, errno,
1642 safe_strerror(errno));
1643 } else {
1644 zlog_warn(
1645 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1646 dst_str, ifname, group_str, msg_size, sent);
1647 }
1648 return;
1649 }
1650
1651 /*
1652 s_flag sanity test: s_flag must be set for general queries
1653
1654 RFC 3376: 6.6.1. Timer Updates
1655
1656 When a router sends or receives a query with a clear Suppress
1657 Router-Side Processing flag, it must update its timers to reflect
1658 the correct timeout values for the group or sources being queried.
1659
1660 General queries don't trigger timer update.
1661 */
1662 if (!s_flag) {
1663 /* general query? */
1664 if (group_addr.s_addr == INADDR_ANY) {
1665 char dst_str[INET_ADDRSTRLEN];
1666 char group_str[INET_ADDRSTRLEN];
1667 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1668 sizeof(dst_str));
1669 pim_inet4_dump("<group?>", group_addr, group_str,
1670 sizeof(group_str));
1671 zlog_warn(
1672 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1673 __func__, dst_str, ifname, group_str,
1674 num_sources);
1675 }
1676 }
1677 }
1678
1679 void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str,
1680 char *igmp_msg)
1681 {
1682 struct interface *ifp;
1683 struct pim_interface *pim_ifp;
1684 struct in_addr group_addr;
1685 uint8_t resv_s_qrv = 0;
1686 uint8_t s_flag = 0;
1687 uint8_t qrv = 0;
1688 int i;
1689
1690 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1691 ifp = igmp->interface;
1692 pim_ifp = ifp->info;
1693
1694 /*
1695 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1696 *
1697 * Routers adopt the QRV value from the most recently received Query
1698 * as their own [Robustness Variable] value, unless that most
1699 * recently received QRV was zero, in which case the receivers use
1700 * the default [Robustness Variable] value specified in section 8.1
1701 * or a statically configured value.
1702 */
1703 resv_s_qrv = igmp_msg[8];
1704 qrv = 7 & resv_s_qrv;
1705 igmp->querier_robustness_variable =
1706 qrv ? qrv : pim_ifp->gm_default_robustness_variable;
1707
1708 /*
1709 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1710 *
1711 * Multicast routers that are not the current querier adopt the QQI
1712 * value from the most recently received Query as their own [Query
1713 * Interval] value, unless that most recently received QQI was zero,
1714 * in which case the receiving routers use the default.
1715 */
1716 if (igmp->t_other_querier_timer) {
1717 /* other querier present */
1718 uint8_t qqic;
1719 uint16_t qqi;
1720 qqic = igmp_msg[9];
1721 qqi = igmp_msg_decode8to16(qqic);
1722 igmp->querier_query_interval =
1723 qqi ? qqi : pim_ifp->gm_default_query_interval;
1724
1725 if (PIM_DEBUG_GM_TRACE) {
1726 char ifaddr_str[INET_ADDRSTRLEN];
1727 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1728 sizeof(ifaddr_str));
1729 zlog_debug(
1730 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1731 ifaddr_str,
1732 qqi ? "recv-non-default" : "default",
1733 igmp->querier_query_interval, qqic, from_str);
1734 }
1735 }
1736
1737 /*
1738 * RFC 3376: 6.6.1. Timer Updates
1739 *
1740 * When a router sends or receives a query with a clear Suppress
1741 * Router-Side Processing flag, it must update its timers to reflect
1742 * the correct timeout values for the group or sources being queried.
1743 *
1744 * General queries don't trigger timer update.
1745 */
1746 s_flag = (1 << 3) & resv_s_qrv;
1747
1748 if (!s_flag) {
1749 /* s_flag is clear */
1750
1751 if (group_addr.s_addr == INADDR_ANY) {
1752 /* this is a general query */
1753 /* log that general query should have the s_flag set */
1754 zlog_warn(
1755 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1756 from_str, ifp->name);
1757 } else {
1758 struct gm_group *group;
1759
1760 /* this is a non-general query: perform timer updates */
1761
1762 group = find_group_by_addr(igmp, group_addr);
1763 if (group) {
1764 int recv_num_sources = ntohs(*(
1765 uint16_t
1766 *)(igmp_msg
1767 + IGMP_V3_NUMSOURCES_OFFSET));
1768
1769 /*
1770 * RFC 3376: 6.6.1. Timer Updates
1771 * Query Q(G,A): Source Timer for sources in A
1772 * are lowered to LMQT
1773 * Query Q(G): Group Timer is lowered to LMQT
1774 */
1775 if (recv_num_sources < 1) {
1776 /* Query Q(G): Group Timer is lowered to
1777 * LMQT */
1778
1779 igmp_group_timer_lower_to_lmqt(group);
1780 } else {
1781 /* Query Q(G,A): Source Timer for
1782 * sources in A are lowered to LMQT */
1783
1784 /* Scan sources in query and lower their
1785 * timers to LMQT */
1786 struct in_addr *sources =
1787 (struct in_addr
1788 *)(igmp_msg
1789 + IGMP_V3_SOURCES_OFFSET);
1790 for (i = 0; i < recv_num_sources; ++i) {
1791 struct in_addr src_addr;
1792 struct gm_source *src;
1793 memcpy(&src_addr, sources + i,
1794 sizeof(struct in_addr));
1795 src = igmp_find_source_by_addr(
1796 group, src_addr);
1797 if (src) {
1798 igmp_source_timer_lower_to_lmqt(
1799 src);
1800 }
1801 }
1802 }
1803 } else {
1804 char group_str[INET_ADDRSTRLEN];
1805 pim_inet4_dump("<group?>", group_addr,
1806 group_str, sizeof(group_str));
1807 zlog_warn(
1808 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1809 from_str, ifp->name, group_str);
1810 }
1811 }
1812 } /* s_flag is clear: timer updates */
1813 }
1814
1815 static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str,
1816 struct in_addr grp, int rec_type)
1817 {
1818 struct pim_interface *pim_ifp;
1819 struct in_addr grp_addr;
1820
1821 pim_ifp = ifp->info;
1822
1823 /* determine filtering status for group */
1824 if (pim_is_group_filtered(pim_ifp, &grp)) {
1825 if (PIM_DEBUG_GM_PACKETS) {
1826 zlog_debug(
1827 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1828 &grp.s_addr, from_str, ifp->name,
1829 pim_ifp->boundary_oil_plist);
1830 }
1831 return false;
1832 }
1833
1834 /*
1835 * If we receive a igmp report with the group in 224.0.0.0/24
1836 * then we should ignore it
1837 */
1838
1839 grp_addr.s_addr = ntohl(grp.s_addr);
1840
1841 if (pim_is_group_224_0_0_0_24(grp_addr)) {
1842 if (PIM_DEBUG_GM_PACKETS) {
1843 zlog_debug(
1844 "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
1845 &grp.s_addr, from_str, ifp->name);
1846 }
1847 return false;
1848 }
1849
1850 /*
1851 * RFC 4604
1852 * section 2.2.1
1853 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
1854 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
1855 * the SSM range.
1856 */
1857 if (pim_is_grp_ssm(pim_ifp->pim, grp)) {
1858 switch (rec_type) {
1859 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1860 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
1861 if (PIM_DEBUG_GM_PACKETS) {
1862 zlog_debug(
1863 "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
1864 &grp.s_addr, from_str, ifp->name);
1865 }
1866 return false;
1867 }
1868 }
1869
1870 return true;
1871 }
1872
1873 int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
1874 const char *from_str, char *igmp_msg, int igmp_msg_len)
1875 {
1876 int num_groups;
1877 uint8_t *group_record;
1878 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1879 struct interface *ifp = igmp->interface;
1880 struct pim_interface *pim_ifp = ifp->info;
1881 int i;
1882
1883 if (igmp->mtrace_only)
1884 return 0;
1885
1886 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1887 zlog_warn(
1888 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1889 from_str, ifp->name, igmp_msg_len,
1890 IGMP_V3_MSG_MIN_SIZE);
1891 return -1;
1892 }
1893
1894 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
1895 zlog_warn(
1896 "Recv IGMPv3 report from %s on %s with invalid checksum",
1897 from_str, ifp->name);
1898 return -1;
1899 }
1900
1901 /* Collecting IGMP Rx stats */
1902 igmp->igmp_stats.report_v3++;
1903
1904 if (pim_ifp->igmp_version == 2) {
1905 zlog_warn(
1906 "Received Version 3 packet but interface: %s is configured for version 2",
1907 ifp->name);
1908 return -1;
1909 }
1910
1911 num_groups = ntohs(
1912 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1913 if (num_groups < 1) {
1914 zlog_warn(
1915 "Recv IGMP report v3 from %s on %s: missing group records",
1916 from_str, ifp->name);
1917 return -1;
1918 }
1919
1920 if (PIM_DEBUG_GM_PACKETS) {
1921 zlog_debug(
1922 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1923 from_str, ifp->name, igmp_msg_len, num_groups);
1924 }
1925
1926 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1927
1928 /* Scan groups */
1929 for (i = 0; i < num_groups; ++i) {
1930 struct in_addr rec_group;
1931 uint8_t *sources;
1932 uint8_t *src;
1933 int rec_type;
1934 int rec_auxdatalen;
1935 int rec_num_sources;
1936 int j;
1937
1938 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1939 > report_pastend) {
1940 zlog_warn(
1941 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1942 from_str, ifp->name);
1943 return -1;
1944 }
1945
1946 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1947 rec_auxdatalen =
1948 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1949 rec_num_sources = ntohs(*(
1950 uint16_t *)(group_record
1951 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1952
1953 memcpy(&rec_group,
1954 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1955 sizeof(struct in_addr));
1956
1957 if (PIM_DEBUG_GM_PACKETS) {
1958 zlog_debug(
1959 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1960 from_str, ifp->name, i, rec_type,
1961 rec_auxdatalen, rec_num_sources,
1962 &rec_group);
1963 }
1964
1965 /* Scan sources */
1966
1967 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1968
1969 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1970
1971 if ((src + 4) > report_pastend) {
1972 zlog_warn(
1973 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1974 from_str, ifp->name);
1975 return -1;
1976 }
1977
1978 if (PIM_DEBUG_GM_PACKETS) {
1979 char src_str[200];
1980
1981 if (!inet_ntop(AF_INET, src, src_str,
1982 sizeof(src_str)))
1983 snprintf(src_str, sizeof(src_str),
1984 "<source?>");
1985
1986 zlog_debug(
1987 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1988 from_str, ifp->name, i,
1989 &rec_group, src_str);
1990 }
1991 } /* for (sources) */
1992
1993
1994 if (igmp_pkt_grp_addr_ok(ifp, from_str, rec_group, rec_type))
1995 switch (rec_type) {
1996 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1997 igmpv3_report_isin(igmp, from, rec_group,
1998 rec_num_sources,
1999 (struct in_addr *)sources);
2000 break;
2001 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
2002 igmpv3_report_isex(
2003 igmp, from, rec_group, rec_num_sources,
2004 (struct in_addr *)sources, 0);
2005 break;
2006 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
2007 igmpv3_report_toin(igmp, from, rec_group,
2008 rec_num_sources,
2009 (struct in_addr *)sources);
2010 break;
2011 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
2012 igmpv3_report_toex(igmp, from, rec_group,
2013 rec_num_sources,
2014 (struct in_addr *)sources);
2015 break;
2016 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2017 igmpv3_report_allow(igmp, from, rec_group,
2018 rec_num_sources,
2019 (struct in_addr *)sources);
2020 break;
2021 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2022 igmpv3_report_block(igmp, from, rec_group,
2023 rec_num_sources,
2024 (struct in_addr *)sources);
2025 break;
2026 default:
2027 zlog_warn(
2028 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2029 from_str, ifp->name, rec_type);
2030 }
2031
2032 group_record +=
2033 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2034
2035 } /* for (group records) */
2036
2037 return 0;
2038 }