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