]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv3.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[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 event *t)
111 {
112 struct gm_source *source;
113 struct gm_group *group;
114
115 source = EVENT_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 EVENT_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 event_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 event *t)
1204 {
1205 struct gm_group *group;
1206 int num_retransmit_sources_left;
1207 int send_with_sflag_set; /* boolean */
1208
1209 group = EVENT_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 event_add_timer_msec(router->master, igmp_group_retransmit, group,
1285 lmqi_msec, &group->t_group_query_retransmit_timer);
1286 }
1287
1288 static long igmp_group_timer_remain_msec(struct gm_group *group)
1289 {
1290 return pim_time_timer_remain_msec(group->t_group_timer);
1291 }
1292
1293 static long igmp_source_timer_remain_msec(struct gm_source *source)
1294 {
1295 return pim_time_timer_remain_msec(source->t_source_timer);
1296 }
1297
1298 /*
1299 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1300 */
1301 static void group_query_send(struct gm_group *group)
1302 {
1303 struct pim_interface *pim_ifp;
1304 long lmqc; /* Last Member Query Count */
1305
1306 pim_ifp = group->interface->info;
1307 lmqc = pim_ifp->gm_last_member_query_count;
1308
1309 /* lower group timer to lmqt */
1310 igmp_group_timer_lower_to_lmqt(group);
1311
1312 /* reset retransmission counter */
1313 group->group_specific_query_retransmit_count = lmqc;
1314
1315 /* immediately send group specific query (decrease retransmit counter by
1316 * 1)*/
1317 group_retransmit_group(group);
1318
1319 /* make sure group retransmit timer is running */
1320 group_retransmit_timer_on(group);
1321 }
1322
1323 /*
1324 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1325 */
1326 static void source_query_send_by_flag(struct gm_group *group,
1327 int num_sources_tosend)
1328 {
1329 struct pim_interface *pim_ifp;
1330 struct listnode *src_node;
1331 struct gm_source *src;
1332 long lmqc; /* Last Member Query Count */
1333 long lmqi_msec; /* Last Member Query Interval */
1334 long lmqt_msec; /* Last Member Query Time */
1335
1336 assert(num_sources_tosend > 0);
1337
1338 pim_ifp = group->interface->info;
1339
1340 lmqc = pim_ifp->gm_last_member_query_count;
1341 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1342 lmqt_msec = lmqc * lmqi_msec;
1343
1344 /*
1345 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1346 Queries
1347
1348 (...) for each of the sources in X of group G, with source timer
1349 larger
1350 than LMQT:
1351 o Set number of retransmissions for each source to [Last Member
1352 Query Count].
1353 o Lower source timer to LMQT.
1354 */
1355 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1356 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1357 /* source "src" in X of group G */
1358 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1359 src->source_query_retransmit_count = lmqc;
1360 igmp_source_timer_lower_to_lmqt(src);
1361 }
1362 }
1363 }
1364
1365 /* send group-and-source specific queries */
1366 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1367
1368 /* make sure group retransmit timer is running */
1369 group_retransmit_timer_on(group);
1370 }
1371
1372 static void block_excl(struct gm_group *group, int num_sources,
1373 struct in_addr *sources)
1374 {
1375 int num_sources_tosend = 0;
1376 int i;
1377
1378 /* 1. clear off SEND flag from all known sources (X,Y) */
1379 source_clear_send_flag(group->group_source_list);
1380
1381 /* 2. scan received sources (A) */
1382 for (i = 0; i < num_sources; ++i) {
1383 struct gm_source *source;
1384 struct in_addr *src_addr;
1385 bool new;
1386
1387 src_addr = sources + i;
1388
1389 /* lookup reported source (A) in known sources (X,Y) */
1390 source = igmp_get_source_by_addr(group, *src_addr, &new);
1391 if (!source)
1392 continue;
1393
1394 if (new) {
1395 /* 3: if not found, create source with Group Timer:
1396 * (A-X-Y)=Group Timer */
1397 long group_timer_msec;
1398
1399 assert(!source->t_source_timer); /* timer == 0 */
1400 group_timer_msec = igmp_group_timer_remain_msec(group);
1401 igmp_source_timer_on(group, source, group_timer_msec);
1402 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1403 }
1404
1405 if (source->t_source_timer) {
1406 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1407 IGMP_SOURCE_DO_SEND(source->source_flags);
1408 ++num_sources_tosend;
1409 }
1410 }
1411
1412 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1413 if (num_sources_tosend > 0) {
1414 source_query_send_by_flag(group, num_sources_tosend);
1415 }
1416 }
1417
1418 static void block_incl(struct gm_group *group, int num_sources,
1419 struct in_addr *sources)
1420 {
1421 int num_sources_tosend = 0;
1422 int i;
1423
1424 /* 1. clear off SEND flag from all known sources (B) */
1425 source_clear_send_flag(group->group_source_list);
1426
1427 /* 2. scan received sources (A) */
1428 for (i = 0; i < num_sources; ++i) {
1429 struct gm_source *source;
1430 struct in_addr *src_addr;
1431
1432 src_addr = sources + i;
1433
1434 /* lookup reported source (A) in known sources (B) */
1435 source = igmp_find_source_by_addr(group, *src_addr);
1436 if (source) {
1437 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1438 IGMP_SOURCE_DO_SEND(source->source_flags);
1439 ++num_sources_tosend;
1440 }
1441 }
1442
1443 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1444 if (num_sources_tosend > 0) {
1445 source_query_send_by_flag(group, num_sources_tosend);
1446 }
1447 }
1448
1449 void igmpv3_report_block(struct gm_sock *igmp, struct in_addr from,
1450 struct in_addr group_addr, int num_sources,
1451 struct in_addr *sources)
1452 {
1453 struct interface *ifp = igmp->interface;
1454 struct gm_group *group;
1455
1456 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
1457
1458 /* non-existent group is created as INCLUDE {empty} */
1459 group = igmp_add_group_by_addr(igmp, group_addr);
1460 if (!group) {
1461 return;
1462 }
1463
1464 if (group->group_filtermode_isexcl) {
1465 /* EXCLUDE mode */
1466 block_excl(group, num_sources, sources);
1467 } else {
1468 /* INCLUDE mode */
1469 block_incl(group, num_sources, sources);
1470 }
1471 }
1472
1473 void igmp_group_timer_lower_to_lmqt(struct gm_group *group)
1474 {
1475 struct interface *ifp;
1476 struct pim_interface *pim_ifp;
1477 char *ifname;
1478 int lmqi_dsec; /* Last Member Query Interval */
1479 int lmqc; /* Last Member Query Count */
1480 int lmqt_msec; /* Last Member Query Time */
1481
1482 /*
1483 RFC 3376: 6.2.2. Definition of Group Timers
1484
1485 The group timer is only used when a group is in EXCLUDE mode and
1486 it represents the time for the *filter-mode* of the group to
1487 expire and switch to INCLUDE mode.
1488 */
1489 if (!group->group_filtermode_isexcl) {
1490 return;
1491 }
1492
1493 ifp = group->interface;
1494 pim_ifp = ifp->info;
1495 ifname = ifp->name;
1496
1497 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1498 lmqc = pim_ifp->gm_last_member_query_count;
1499 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1500 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1501
1502 if (PIM_DEBUG_GM_TRACE) {
1503 char group_str[INET_ADDRSTRLEN];
1504 pim_inet4_dump("<group?>", group->group_addr, group_str,
1505 sizeof(group_str));
1506 zlog_debug(
1507 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1508 __func__, group_str, ifname, lmqc, lmqi_dsec,
1509 lmqt_msec);
1510 }
1511
1512 assert(group->group_filtermode_isexcl);
1513
1514 igmp_group_timer_on(group, lmqt_msec, ifname);
1515 }
1516
1517 void igmp_source_timer_lower_to_lmqt(struct gm_source *source)
1518 {
1519 struct gm_group *group;
1520 struct interface *ifp;
1521 struct pim_interface *pim_ifp;
1522 char *ifname;
1523 int lmqi_dsec; /* Last Member Query Interval */
1524 int lmqc; /* Last Member Query Count */
1525 int lmqt_msec; /* Last Member Query Time */
1526
1527 group = source->source_group;
1528 ifp = group->interface;
1529 pim_ifp = ifp->info;
1530 ifname = ifp->name;
1531
1532 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1533 lmqc = pim_ifp->gm_last_member_query_count;
1534 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1535 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1536
1537 if (PIM_DEBUG_GM_TRACE) {
1538 char group_str[INET_ADDRSTRLEN];
1539 char source_str[INET_ADDRSTRLEN];
1540 pim_inet4_dump("<group?>", group->group_addr, group_str,
1541 sizeof(group_str));
1542 pim_inet4_dump("<source?>", source->source_addr, source_str,
1543 sizeof(source_str));
1544 zlog_debug(
1545 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1546 __func__, group_str, source_str, ifname, lmqc,
1547 lmqi_dsec, lmqt_msec);
1548 }
1549
1550 igmp_source_timer_on(group, source, lmqt_msec);
1551 }
1552
1553 void igmp_v3_send_query(struct gm_group *group, int fd, const char *ifname,
1554 char *query_buf, int query_buf_size, int num_sources,
1555 struct in_addr dst_addr, struct in_addr group_addr,
1556 int query_max_response_time_dsec, uint8_t s_flag,
1557 uint8_t querier_robustness_variable,
1558 uint16_t querier_query_interval)
1559 {
1560 ssize_t msg_size;
1561 uint8_t max_resp_code;
1562 uint8_t qqic;
1563 ssize_t sent;
1564 struct sockaddr_in to;
1565 socklen_t tolen;
1566 uint16_t checksum;
1567
1568 assert(num_sources >= 0);
1569
1570 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1571 if (msg_size > query_buf_size) {
1572 flog_err(
1573 EC_LIB_DEVELOPMENT,
1574 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1575 __FILE__, __func__, msg_size, query_buf_size);
1576 return;
1577 }
1578
1579 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1580 assert((s_flag == 0) || (s_flag == 1));
1581
1582 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1583 qqic = igmp_msg_encode16to8(querier_query_interval);
1584
1585 /*
1586 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1587
1588 If non-zero, the QRV field contains the [Robustness Variable]
1589 value used by the querier, i.e., the sender of the Query. If the
1590 querier's [Robustness Variable] exceeds 7, the maximum value of
1591 the QRV field, the QRV is set to zero.
1592 */
1593 if (querier_robustness_variable > 7) {
1594 querier_robustness_variable = 0;
1595 }
1596
1597 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1598 query_buf[1] = max_resp_code;
1599 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1600 0; /* for computing checksum */
1601 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1602
1603 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1604 query_buf[9] = qqic;
1605 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1606 htons(num_sources);
1607
1608 checksum = in_cksum(query_buf, msg_size);
1609 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1610
1611 if (PIM_DEBUG_GM_PACKETS) {
1612 char dst_str[INET_ADDRSTRLEN];
1613 char group_str[INET_ADDRSTRLEN];
1614 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1615 pim_inet4_dump("<group?>", group_addr, group_str,
1616 sizeof(group_str));
1617 zlog_debug(
1618 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1619 dst_str, ifname, group_str, num_sources, msg_size,
1620 s_flag, querier_robustness_variable,
1621 querier_query_interval, qqic);
1622 }
1623
1624 memset(&to, 0, sizeof(to));
1625 to.sin_family = AF_INET;
1626 to.sin_addr = dst_addr;
1627 tolen = sizeof(to);
1628
1629 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1630 (struct sockaddr *)&to, tolen);
1631 if (sent != (ssize_t)msg_size) {
1632 char dst_str[INET_ADDRSTRLEN];
1633 char group_str[INET_ADDRSTRLEN];
1634 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1635 pim_inet4_dump("<group?>", group_addr, group_str,
1636 sizeof(group_str));
1637 if (sent < 0) {
1638 zlog_warn(
1639 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1640 dst_str, ifname, group_str, msg_size, errno,
1641 safe_strerror(errno));
1642 } else {
1643 zlog_warn(
1644 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1645 dst_str, ifname, group_str, msg_size, sent);
1646 }
1647 return;
1648 }
1649
1650 /*
1651 s_flag sanity test: s_flag must be set for general queries
1652
1653 RFC 3376: 6.6.1. Timer Updates
1654
1655 When a router sends or receives a query with a clear Suppress
1656 Router-Side Processing flag, it must update its timers to reflect
1657 the correct timeout values for the group or sources being queried.
1658
1659 General queries don't trigger timer update.
1660 */
1661 if (!s_flag) {
1662 /* general query? */
1663 if (group_addr.s_addr == INADDR_ANY) {
1664 char dst_str[INET_ADDRSTRLEN];
1665 char group_str[INET_ADDRSTRLEN];
1666 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1667 sizeof(dst_str));
1668 pim_inet4_dump("<group?>", group_addr, group_str,
1669 sizeof(group_str));
1670 zlog_warn(
1671 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1672 __func__, dst_str, ifname, group_str,
1673 num_sources);
1674 }
1675 }
1676 }
1677
1678 void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str,
1679 char *igmp_msg)
1680 {
1681 struct interface *ifp;
1682 struct pim_interface *pim_ifp;
1683 struct in_addr group_addr;
1684 uint8_t resv_s_qrv = 0;
1685 uint8_t s_flag = 0;
1686 uint8_t qrv = 0;
1687 int i;
1688
1689 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1690 ifp = igmp->interface;
1691 pim_ifp = ifp->info;
1692
1693 /*
1694 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1695 *
1696 * Routers adopt the QRV value from the most recently received Query
1697 * as their own [Robustness Variable] value, unless that most
1698 * recently received QRV was zero, in which case the receivers use
1699 * the default [Robustness Variable] value specified in section 8.1
1700 * or a statically configured value.
1701 */
1702 resv_s_qrv = igmp_msg[8];
1703 qrv = 7 & resv_s_qrv;
1704 igmp->querier_robustness_variable =
1705 qrv ? qrv : pim_ifp->gm_default_robustness_variable;
1706
1707 /*
1708 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1709 *
1710 * Multicast routers that are not the current querier adopt the QQI
1711 * value from the most recently received Query as their own [Query
1712 * Interval] value, unless that most recently received QQI was zero,
1713 * in which case the receiving routers use the default.
1714 */
1715 if (igmp->t_other_querier_timer) {
1716 /* other querier present */
1717 uint8_t qqic;
1718 uint16_t qqi;
1719 qqic = igmp_msg[9];
1720 qqi = igmp_msg_decode8to16(qqic);
1721 igmp->querier_query_interval =
1722 qqi ? qqi : pim_ifp->gm_default_query_interval;
1723
1724 if (PIM_DEBUG_GM_TRACE) {
1725 char ifaddr_str[INET_ADDRSTRLEN];
1726 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1727 sizeof(ifaddr_str));
1728 zlog_debug(
1729 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1730 ifaddr_str,
1731 qqi ? "recv-non-default" : "default",
1732 igmp->querier_query_interval, qqic, from_str);
1733 }
1734 }
1735
1736 /*
1737 * RFC 3376: 6.6.1. Timer Updates
1738 *
1739 * When a router sends or receives a query with a clear Suppress
1740 * Router-Side Processing flag, it must update its timers to reflect
1741 * the correct timeout values for the group or sources being queried.
1742 *
1743 * General queries don't trigger timer update.
1744 */
1745 s_flag = (1 << 3) & resv_s_qrv;
1746
1747 if (!s_flag) {
1748 /* s_flag is clear */
1749
1750 if (group_addr.s_addr == INADDR_ANY) {
1751 /* this is a general query */
1752 /* log that general query should have the s_flag set */
1753 zlog_warn(
1754 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1755 from_str, ifp->name);
1756 } else {
1757 struct gm_group *group;
1758
1759 /* this is a non-general query: perform timer updates */
1760
1761 group = find_group_by_addr(igmp, group_addr);
1762 if (group) {
1763 int recv_num_sources = ntohs(*(
1764 uint16_t
1765 *)(igmp_msg
1766 + IGMP_V3_NUMSOURCES_OFFSET));
1767
1768 /*
1769 * RFC 3376: 6.6.1. Timer Updates
1770 * Query Q(G,A): Source Timer for sources in A
1771 * are lowered to LMQT
1772 * Query Q(G): Group Timer is lowered to LMQT
1773 */
1774 if (recv_num_sources < 1) {
1775 /* Query Q(G): Group Timer is lowered to
1776 * LMQT */
1777
1778 igmp_group_timer_lower_to_lmqt(group);
1779 } else {
1780 /* Query Q(G,A): Source Timer for
1781 * sources in A are lowered to LMQT */
1782
1783 /* Scan sources in query and lower their
1784 * timers to LMQT */
1785 struct in_addr *sources =
1786 (struct in_addr
1787 *)(igmp_msg
1788 + IGMP_V3_SOURCES_OFFSET);
1789 for (i = 0; i < recv_num_sources; ++i) {
1790 struct in_addr src_addr;
1791 struct gm_source *src;
1792 memcpy(&src_addr, sources + i,
1793 sizeof(struct in_addr));
1794 src = igmp_find_source_by_addr(
1795 group, src_addr);
1796 if (src) {
1797 igmp_source_timer_lower_to_lmqt(
1798 src);
1799 }
1800 }
1801 }
1802 } else {
1803 char group_str[INET_ADDRSTRLEN];
1804 pim_inet4_dump("<group?>", group_addr,
1805 group_str, sizeof(group_str));
1806 zlog_warn(
1807 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1808 from_str, ifp->name, group_str);
1809 }
1810 }
1811 } /* s_flag is clear: timer updates */
1812 }
1813
1814 static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str,
1815 struct in_addr grp, int rec_type)
1816 {
1817 struct pim_interface *pim_ifp;
1818 struct in_addr grp_addr;
1819
1820 pim_ifp = ifp->info;
1821
1822 /* determine filtering status for group */
1823 if (pim_is_group_filtered(pim_ifp, &grp)) {
1824 if (PIM_DEBUG_GM_PACKETS) {
1825 zlog_debug(
1826 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1827 &grp.s_addr, from_str, ifp->name,
1828 pim_ifp->boundary_oil_plist);
1829 }
1830 return false;
1831 }
1832
1833 /*
1834 * If we receive a igmp report with the group in 224.0.0.0/24
1835 * then we should ignore it
1836 */
1837
1838 grp_addr.s_addr = ntohl(grp.s_addr);
1839
1840 if (pim_is_group_224_0_0_0_24(grp_addr)) {
1841 if (PIM_DEBUG_GM_PACKETS) {
1842 zlog_debug(
1843 "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
1844 &grp.s_addr, from_str, ifp->name);
1845 }
1846 return false;
1847 }
1848
1849 /*
1850 * RFC 4604
1851 * section 2.2.1
1852 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
1853 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
1854 * the SSM range.
1855 */
1856 if (pim_is_grp_ssm(pim_ifp->pim, grp)) {
1857 switch (rec_type) {
1858 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1859 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
1860 if (PIM_DEBUG_GM_PACKETS) {
1861 zlog_debug(
1862 "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
1863 &grp.s_addr, from_str, ifp->name);
1864 }
1865 return false;
1866 }
1867 }
1868
1869 return true;
1870 }
1871
1872 int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
1873 const char *from_str, char *igmp_msg, int igmp_msg_len)
1874 {
1875 int num_groups;
1876 uint8_t *group_record;
1877 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1878 struct interface *ifp = igmp->interface;
1879 struct pim_interface *pim_ifp = ifp->info;
1880 int i;
1881
1882 if (igmp->mtrace_only)
1883 return 0;
1884
1885 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1886 zlog_warn(
1887 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1888 from_str, ifp->name, igmp_msg_len,
1889 IGMP_V3_MSG_MIN_SIZE);
1890 return -1;
1891 }
1892
1893 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
1894 zlog_warn(
1895 "Recv IGMPv3 report from %s on %s with invalid checksum",
1896 from_str, ifp->name);
1897 return -1;
1898 }
1899
1900 /* Collecting IGMP Rx stats */
1901 igmp->igmp_stats.report_v3++;
1902
1903 if (pim_ifp->igmp_version == 2) {
1904 zlog_warn(
1905 "Received Version 3 packet but interface: %s is configured for version 2",
1906 ifp->name);
1907 return -1;
1908 }
1909
1910 num_groups = ntohs(
1911 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1912 if (num_groups < 1) {
1913 zlog_warn(
1914 "Recv IGMP report v3 from %s on %s: missing group records",
1915 from_str, ifp->name);
1916 return -1;
1917 }
1918
1919 if (PIM_DEBUG_GM_PACKETS) {
1920 zlog_debug(
1921 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1922 from_str, ifp->name, igmp_msg_len, num_groups);
1923 }
1924
1925 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1926
1927 /* Scan groups */
1928 for (i = 0; i < num_groups; ++i) {
1929 struct in_addr rec_group;
1930 uint8_t *sources;
1931 uint8_t *src;
1932 int rec_type;
1933 int rec_auxdatalen;
1934 int rec_num_sources;
1935 int j;
1936
1937 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1938 > report_pastend) {
1939 zlog_warn(
1940 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1941 from_str, ifp->name);
1942 return -1;
1943 }
1944
1945 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1946 rec_auxdatalen =
1947 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1948 rec_num_sources = ntohs(*(
1949 uint16_t *)(group_record
1950 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1951
1952 memcpy(&rec_group,
1953 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1954 sizeof(struct in_addr));
1955
1956 if (PIM_DEBUG_GM_PACKETS) {
1957 zlog_debug(
1958 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1959 from_str, ifp->name, i, rec_type,
1960 rec_auxdatalen, rec_num_sources,
1961 &rec_group);
1962 }
1963
1964 /* Scan sources */
1965
1966 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1967
1968 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1969
1970 if ((src + 4) > report_pastend) {
1971 zlog_warn(
1972 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1973 from_str, ifp->name);
1974 return -1;
1975 }
1976
1977 if (PIM_DEBUG_GM_PACKETS) {
1978 char src_str[200];
1979
1980 if (!inet_ntop(AF_INET, src, src_str,
1981 sizeof(src_str)))
1982 snprintf(src_str, sizeof(src_str),
1983 "<source?>");
1984
1985 zlog_debug(
1986 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1987 from_str, ifp->name, i,
1988 &rec_group, src_str);
1989 }
1990 } /* for (sources) */
1991
1992
1993 if (igmp_pkt_grp_addr_ok(ifp, from_str, rec_group, rec_type))
1994 switch (rec_type) {
1995 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1996 igmpv3_report_isin(igmp, from, rec_group,
1997 rec_num_sources,
1998 (struct in_addr *)sources);
1999 break;
2000 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
2001 igmpv3_report_isex(
2002 igmp, from, rec_group, rec_num_sources,
2003 (struct in_addr *)sources, 0);
2004 break;
2005 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
2006 igmpv3_report_toin(igmp, from, rec_group,
2007 rec_num_sources,
2008 (struct in_addr *)sources);
2009 break;
2010 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
2011 igmpv3_report_toex(igmp, from, rec_group,
2012 rec_num_sources,
2013 (struct in_addr *)sources);
2014 break;
2015 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2016 igmpv3_report_allow(igmp, from, rec_group,
2017 rec_num_sources,
2018 (struct in_addr *)sources);
2019 break;
2020 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2021 igmpv3_report_block(igmp, from, rec_group,
2022 rec_num_sources,
2023 (struct in_addr *)sources);
2024 break;
2025 default:
2026 zlog_warn(
2027 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2028 from_str, ifp->name, rec_type);
2029 }
2030
2031 group_record +=
2032 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2033
2034 } /* for (group records) */
2035
2036 return 0;
2037 }