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