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