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