Coverage Report

Created: 2025-12-12 06:43

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