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