Coverage Report

Created: 2026-01-01 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_igmp.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
9
#include "memory.h"
10
#include "prefix.h"
11
#include "if.h"
12
#include "hash.h"
13
#include "jhash.h"
14
#include "lib_errors.h"
15
16
#include "pimd.h"
17
#include "pim_instance.h"
18
#include "pim_igmp.h"
19
#include "pim_igmpv2.h"
20
#include "pim_igmpv3.h"
21
#include "pim_igmp_mtrace.h"
22
#include "pim_iface.h"
23
#include "pim_sock.h"
24
#include "pim_mroute.h"
25
#include "pim_str.h"
26
#include "pim_util.h"
27
#include "pim_time.h"
28
#include "pim_ssm.h"
29
#include "pim_tib.h"
30
31
static void group_timer_off(struct gm_group *group);
32
static void pim_igmp_general_query(struct event *t);
33
34
void igmp_anysource_forward_start(struct pim_instance *pim,
35
          struct gm_group *group)
36
0
{
37
0
  struct gm_source *source;
38
0
  struct in_addr src_addr = {.s_addr = 0};
39
  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
40
0
  assert(group->group_filtermode_isexcl);
41
0
  assert(listcount(group->group_source_list) < 1);
42
43
0
  source = igmp_get_source_by_addr(group, src_addr, NULL);
44
0
  if (!source) {
45
0
    zlog_warn("%s: Failure to create * source", __func__);
46
0
    return;
47
0
  }
48
49
0
  igmp_source_forward_start(pim, source);
50
0
}
51
52
void igmp_anysource_forward_stop(struct gm_group *group)
53
0
{
54
0
  struct gm_source *source;
55
0
  struct in_addr star = {.s_addr = 0};
56
57
0
  source = igmp_find_source_by_addr(group, star);
58
0
  if (source)
59
0
    igmp_source_forward_stop(source);
60
0
}
61
62
static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
63
                 struct gm_source *source,
64
                 int is_grp_ssm)
65
0
{
66
0
  pim_sgaddr sg;
67
0
  struct gm_group *group = source->source_group;
68
69
0
  memset(&sg, 0, sizeof(sg));
70
0
  sg.src = source->source_addr;
71
0
  sg.grp = group->group_addr;
72
73
  /** if there is no PIM state **/
74
0
  if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
75
0
    if (pim_addr_is_any(source->source_addr)) {
76
0
      if (is_grp_ssm) {
77
0
        if (PIM_DEBUG_PIM_EVENTS)
78
0
          zlog_debug(
79
0
            "local membership del for %pSG as G is now SSM",
80
0
            &sg);
81
0
        igmp_source_forward_stop(source);
82
0
      }
83
0
    } else {
84
0
      if (!is_grp_ssm) {
85
0
        if (PIM_DEBUG_PIM_EVENTS)
86
0
          zlog_debug(
87
0
            "local membership del for %pSG as G is now ASM",
88
0
            &sg);
89
0
        igmp_source_forward_stop(source);
90
0
      }
91
0
    }
92
0
  } else {
93
0
    if (!pim_addr_is_any(source->source_addr) && (is_grp_ssm)) {
94
0
      if (PIM_DEBUG_PIM_EVENTS)
95
0
        zlog_debug(
96
0
          "local membership add for %pSG as G is now SSM",
97
0
          &sg);
98
0
      igmp_source_forward_start(pim, source);
99
0
    }
100
0
  }
101
0
}
102
103
void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
104
0
{
105
0
  struct interface *ifp;
106
107
0
  FOR_ALL_INTERFACES (pim->vrf, ifp) {
108
0
    struct pim_interface *pim_ifp = ifp->info;
109
0
    struct listnode *grpnode, *grp_nextnode;
110
0
    struct gm_group *grp;
111
0
    struct pim_ifchannel *ch, *ch_temp;
112
113
0
    if (!pim_ifp)
114
0
      continue;
115
116
    /* scan igmp groups */
117
0
    for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grpnode,
118
0
               grp_nextnode, grp)) {
119
0
      struct listnode *srcnode;
120
0
      struct gm_source *src;
121
0
      int is_grp_ssm;
122
123
      /*
124
       * RFC 4604
125
       * section 2.2.1
126
       * EXCLUDE mode does not apply to SSM addresses,
127
       * and an SSM-aware router will ignore
128
       * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
129
       * requests in the SSM range.
130
       */
131
0
      is_grp_ssm = pim_is_grp_ssm(pim, grp->group_addr);
132
0
      if (is_grp_ssm && grp->group_filtermode_isexcl) {
133
0
        igmp_group_delete(grp);
134
0
      } else {
135
        /* scan group sources */
136
0
        for (ALL_LIST_ELEMENTS_RO(
137
0
               grp->group_source_list, srcnode,
138
0
               src)) {
139
0
          igmp_source_forward_reevaluate_one(
140
0
            pim, src, is_grp_ssm);
141
0
        } /* scan group sources */
142
0
      }
143
0
    } /* scan igmp groups */
144
145
0
    RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
146
0
         ch_temp) {
147
0
      if (pim_is_grp_ssm(pim, ch->sg.grp)) {
148
0
        if (pim_addr_is_any(ch->sg.src))
149
0
          pim_ifchannel_delete(ch);
150
0
      }
151
0
    }
152
0
  } /* scan interfaces */
153
0
}
154
155
void igmp_source_forward_start(struct pim_instance *pim,
156
             struct gm_source *source)
157
0
{
158
0
  struct gm_group *group;
159
0
  pim_sgaddr sg;
160
161
0
  memset(&sg, 0, sizeof(sg));
162
0
  sg.src = source->source_addr;
163
0
  sg.grp = source->source_group->group_addr;
164
165
0
  if (PIM_DEBUG_GM_TRACE) {
166
0
    zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
167
0
         source->source_group->interface->name,
168
0
         IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
169
0
  }
170
171
  /*
172
   * PIM state should not be allowed for ASM group with valid source
173
   * address.
174
   */
175
0
  if ((!pim_is_grp_ssm(pim, source->source_group->group_addr)) &&
176
0
      !pim_addr_is_any(source->source_addr)) {
177
0
    zlog_warn(
178
0
      "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
179
0
      __func__, &sg);
180
0
    return;
181
0
  }
182
183
  /* Prevent IGMP interface from installing multicast route multiple
184
     times */
185
0
  if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
186
0
    return;
187
0
  }
188
189
0
  group = source->source_group;
190
191
0
  if (tib_sg_gm_join(pim, sg, group->interface,
192
0
         &source->source_channel_oil))
193
0
    IGMP_SOURCE_DO_FORWARDING(source->source_flags);
194
0
}
195
196
/*
197
  igmp_source_forward_stop: stop forwarding, but keep the source
198
  igmp_source_delete:       stop forwarding, and delete the source
199
 */
200
void igmp_source_forward_stop(struct gm_source *source)
201
0
{
202
0
  struct pim_interface *pim_oif;
203
0
  struct gm_group *group;
204
0
  pim_sgaddr sg;
205
206
0
  memset(&sg, 0, sizeof(sg));
207
0
  sg.src = source->source_addr;
208
0
  sg.grp = source->source_group->group_addr;
209
210
0
  if (PIM_DEBUG_GM_TRACE) {
211
0
    zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
212
0
         source->source_group->interface->name,
213
0
         IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
214
0
  }
215
216
  /* Prevent IGMP interface from removing multicast route multiple
217
     times */
218
0
  if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
219
0
    return;
220
0
  }
221
222
0
  group = source->source_group;
223
0
  pim_oif = group->interface->info;
224
225
0
  tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
226
0
      &source->source_channel_oil);
227
0
  IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
228
0
}
229
230
/* This socket is used for TXing IGMP packets only, IGMP RX happens
231
 * in pim_mroute_msg()
232
 */
233
static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp)
234
0
{
235
0
  int fd;
236
0
  int join = 0;
237
0
  struct in_addr group;
238
0
  struct pim_interface *pim_ifp = ifp->info;
239
0
240
0
  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
241
0
242
0
  if (fd < 0)
243
0
    return -1;
244
0
245
0
  if (inet_aton(PIM_ALL_ROUTERS, &group)) {
246
0
    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
247
0
      ++join;
248
0
  } else {
249
0
    zlog_warn(
250
0
      "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
251
0
      __FILE__, __func__, fd, &ifaddr, PIM_ALL_ROUTERS, errno,
252
0
      safe_strerror(errno));
253
0
  }
254
0
255
0
  /*
256
0
    IGMP routers periodically send IGMP general queries to
257
0
    AllSystems=224.0.0.1
258
0
    IGMP routers must receive general queries for querier election.
259
0
  */
260
0
  if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
261
0
    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
262
0
      ++join;
263
0
  } else {
264
0
    zlog_warn(
265
0
      "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
266
0
      __FILE__, __func__, fd, &ifaddr,
267
0
      PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
268
0
  }
269
0
270
0
  if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
271
0
    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
272
0
             pim_ifp)) {
273
0
      ++join;
274
0
    }
275
0
  } else {
276
0
    zlog_warn(
277
0
      "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
278
0
      __FILE__, __func__, fd, &ifaddr,
279
0
      PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
280
0
  }
281
0
282
0
  if (!join) {
283
0
    flog_err_sys(
284
0
      EC_LIB_SOCKET,
285
0
      "IGMP socket fd=%d could not join any group on interface address %pI4",
286
0
      fd, &ifaddr);
287
0
    close(fd);
288
0
    fd = -1;
289
0
  }
290
0
291
0
  return fd;
292
0
}
293
294
#undef IGMP_SOCK_DUMP
295
296
#ifdef IGMP_SOCK_DUMP
297
static void igmp_sock_dump(array_t *igmp_sock_array)
298
{
299
  int size = array_size(igmp_sock_array);
300
  for (int i = 0; i < size; ++i) {
301
302
    struct gm_sock *igmp = array_get(igmp_sock_array, i);
303
304
    zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__,
305
         __func__, i, size, &igmp->ifaddr,
306
         igmp->fd);
307
  }
308
}
309
#endif
310
311
struct gm_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
312
              struct in_addr ifaddr)
313
0
{
314
0
  struct listnode *sock_node;
315
0
  struct gm_sock *igmp;
316
317
#ifdef IGMP_SOCK_DUMP
318
  igmp_sock_dump(igmp_sock_list);
319
#endif
320
321
0
  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
322
0
    if (ifaddr.s_addr == igmp->ifaddr.s_addr)
323
0
      return igmp;
324
325
0
  return NULL;
326
0
}
327
328
static void pim_igmp_other_querier_expire(struct event *t)
329
0
{
330
0
  struct gm_sock *igmp;
331
0
332
0
  igmp = EVENT_ARG(t);
333
0
334
0
  assert(!igmp->t_igmp_query_timer);
335
0
336
0
  if (PIM_DEBUG_GM_TRACE) {
337
0
    char ifaddr_str[INET_ADDRSTRLEN];
338
0
    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
339
0
             sizeof(ifaddr_str));
340
0
    zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
341
0
  }
342
0
  /* Mark the interface address as querier address */
343
0
  igmp->querier_addr = igmp->ifaddr;
344
0
345
0
  /*
346
0
    We are the current querier, then
347
0
    re-start sending general queries.
348
0
    RFC 2236 - sec 7 Other Querier
349
0
    present timer expired (Send General
350
0
    Query, Set Gen. Query. timer)
351
0
  */
352
0
  pim_igmp_general_query(t);
353
0
}
354
355
void pim_igmp_other_querier_timer_on(struct gm_sock *igmp)
356
0
{
357
0
  long other_querier_present_interval_msec;
358
0
  struct pim_interface *pim_ifp;
359
360
0
  assert(igmp);
361
0
  assert(igmp->interface);
362
0
  assert(igmp->interface->info);
363
364
0
  pim_ifp = igmp->interface->info;
365
366
0
  if (igmp->t_other_querier_timer) {
367
    /*
368
      There is other querier present already,
369
      then reset the other-querier-present timer.
370
    */
371
372
0
    if (PIM_DEBUG_GM_TRACE) {
373
0
      char ifaddr_str[INET_ADDRSTRLEN];
374
0
      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
375
0
               sizeof(ifaddr_str));
376
0
      zlog_debug(
377
0
        "Querier %s resetting TIMER event for Other-Querier-Present",
378
0
        ifaddr_str);
379
0
    }
380
0
    EVENT_OFF(igmp->t_other_querier_timer);
381
0
  } else {
382
    /*
383
      We are the current querier, then stop sending general queries:
384
      igmp->t_igmp_query_timer = NULL;
385
    */
386
0
    pim_igmp_general_query_off(igmp);
387
0
  }
388
389
  /*
390
    Since this socket is starting the other-querier-present timer,
391
    there should not be periodic query timer for this socket.
392
   */
393
0
  assert(!igmp->t_igmp_query_timer);
394
395
  /*
396
    RFC 3376: 8.5. Other Querier Present Interval
397
398
    The Other Querier Present Interval is the length of time that must
399
    pass before a multicast router decides that there is no longer
400
    another multicast router which should be the querier.  This value
401
    MUST be ((the Robustness Variable) times (the Query Interval)) plus
402
    (one half of one Query Response Interval).
403
404
    other_querier_present_interval_msec = \
405
      igmp->querier_robustness_variable * \
406
      1000 * igmp->querier_query_interval + \
407
      100 * (pim_ifp->query_max_response_time_dsec >> 1);
408
  */
409
0
  other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
410
0
    igmp->querier_robustness_variable, igmp->querier_query_interval,
411
0
    pim_ifp->gm_query_max_response_time_dsec);
412
413
0
  if (PIM_DEBUG_GM_TRACE) {
414
0
    char ifaddr_str[INET_ADDRSTRLEN];
415
0
    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
416
0
             sizeof(ifaddr_str));
417
0
    zlog_debug(
418
0
      "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
419
0
      ifaddr_str, other_querier_present_interval_msec / 1000,
420
0
      other_querier_present_interval_msec % 1000);
421
0
  }
422
423
0
  event_add_timer_msec(router->master, pim_igmp_other_querier_expire,
424
0
           igmp, other_querier_present_interval_msec,
425
0
           &igmp->t_other_querier_timer);
426
0
}
427
428
void pim_igmp_other_querier_timer_off(struct gm_sock *igmp)
429
0
{
430
0
  assert(igmp);
431
432
0
  if (PIM_DEBUG_GM_TRACE) {
433
0
    if (igmp->t_other_querier_timer) {
434
0
      char ifaddr_str[INET_ADDRSTRLEN];
435
0
      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
436
0
               sizeof(ifaddr_str));
437
0
      zlog_debug(
438
0
        "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
439
0
        ifaddr_str, igmp->fd, igmp->interface->name);
440
0
    }
441
0
  }
442
0
  EVENT_OFF(igmp->t_other_querier_timer);
443
0
}
444
445
int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len)
446
0
{
447
0
  uint16_t recv_checksum;
448
0
  uint16_t checksum;
449
450
0
  IGMP_GET_INT16((unsigned char *)(igmp_msg + IGMP_CHECKSUM_OFFSET),
451
0
           recv_checksum);
452
453
  /* Clear the checksum field */
454
0
  memset(igmp_msg + IGMP_CHECKSUM_OFFSET, 0, 2);
455
456
0
  checksum = in_cksum(igmp_msg, igmp_msg_len);
457
0
  if (ntohs(checksum) != recv_checksum) {
458
0
    zlog_warn("Invalid checksum received %x, calculated %x",
459
0
        recv_checksum, ntohs(checksum));
460
0
    return -1;
461
0
  }
462
463
0
  return 0;
464
0
}
465
466
static int igmp_recv_query(struct gm_sock *igmp, int query_version,
467
         int max_resp_code, struct in_addr from,
468
         const char *from_str, char *igmp_msg,
469
         int igmp_msg_len)
470
0
{
471
0
  struct interface *ifp;
472
0
  struct pim_interface *pim_ifp;
473
0
  struct in_addr group_addr;
474
475
0
  if (igmp->mtrace_only)
476
0
    return 0;
477
478
0
  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
479
480
0
  ifp = igmp->interface;
481
0
  pim_ifp = ifp->info;
482
483
0
  if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
484
0
    zlog_warn(
485
0
      "Recv IGMP query v%d from %s on %s with invalid checksum",
486
0
      query_version, from_str, ifp->name);
487
0
    return -1;
488
0
  }
489
490
0
  if (!pim_if_connected_to_source(ifp, from)) {
491
0
    if (PIM_DEBUG_GM_PACKETS)
492
0
      zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
493
0
           ifp->name, from_str);
494
0
    return 0;
495
0
  }
496
497
0
  if (if_address_is_local(&from, AF_INET, ifp->vrf->vrf_id)) {
498
0
    if (PIM_DEBUG_GM_PACKETS)
499
0
      zlog_debug("Recv IGMP query on interface: %s from ourself %s",
500
0
           ifp->name, from_str);
501
0
    return 0;
502
0
  }
503
504
  /* Collecting IGMP Rx stats */
505
0
  switch (query_version) {
506
0
  case 1:
507
0
    igmp->igmp_stats.query_v1++;
508
0
    break;
509
0
  case 2:
510
0
    igmp->igmp_stats.query_v2++;
511
0
    break;
512
0
  case 3:
513
0
    igmp->igmp_stats.query_v3++;
514
0
    break;
515
0
  default:
516
0
    igmp->igmp_stats.unsupported++;
517
0
  }
518
519
  /*
520
   * RFC 3376 defines some guidelines on operating in backwards
521
   * compatibility with older versions of IGMP but there are some gaps in
522
   * the logic:
523
   *
524
   * - once we drop from say version 3 to version 2 we will never go back
525
   *   to version 3 even if the node that TXed an IGMP v2 query upgrades
526
   *   to v3
527
   *
528
   * - The node with the lowest IP is the querier so we will only know to
529
   *   drop from v3 to v2 if the node that is the querier is also the one
530
   *   that is running igmp v2.  If a non-querier only supports igmp v2
531
   *   we will have no way of knowing.
532
   *
533
   * For now we will simplify things and inform the user that they need to
534
   * configure all PIM routers to use the same version of IGMP.
535
   */
536
0
  if (query_version != pim_ifp->igmp_version) {
537
0
    zlog_warn(
538
0
      "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
539
0
      query_version, from_str, ifp->name,
540
0
      pim_ifp->igmp_version);
541
0
    return 0;
542
0
  }
543
544
0
  if (PIM_DEBUG_GM_PACKETS) {
545
0
    char group_str[INET_ADDRSTRLEN];
546
0
    pim_inet4_dump("<group?>", group_addr, group_str,
547
0
             sizeof(group_str));
548
0
    zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
549
0
         query_version, from_str, ifp->name, group_str);
550
0
  }
551
552
  /*
553
    RFC 3376: 6.6.2. Querier Election
554
555
    When a router receives a query with a lower IP address, it sets
556
    the Other-Querier-Present timer to Other Querier Present Interval
557
    and ceases to send queries on the network if it was the previously
558
    elected querier.
559
   */
560
0
  if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
561
562
    /* As per RFC 2236 section 3:
563
     * When a Querier receives a Leave Group message for a group
564
     * that has group members on the reception interface, it sends
565
     * [Last Member Query Count] Group-Specific Queries every [Last
566
     * Member Query Interval] to the group being left.  These
567
     * Group-Specific Queries have their Max Response time set to
568
     * [Last Member Query Interval].  If no Reports are received
569
     * after the response time of the last query expires, the
570
     * routers assume that the group has no local members, as above.
571
     * Any Querier to non-Querier transition is ignored during this
572
     * time; the same router keeps sending the Group-Specific
573
     * Queries.
574
     */
575
0
    const struct gm_group *group;
576
0
    const struct listnode *grpnode;
577
578
0
    for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
579
0
            group)) {
580
0
      if (!group->t_group_query_retransmit_timer)
581
0
        continue;
582
583
0
      if (PIM_DEBUG_GM_TRACE)
584
0
        zlog_debug(
585
0
          "%s: lower address query packet from %s is ignored when last member query interval timer is running",
586
0
          ifp->name, from_str);
587
0
      return 0;
588
0
    }
589
590
0
    if (PIM_DEBUG_GM_TRACE) {
591
0
      char ifaddr_str[INET_ADDRSTRLEN];
592
0
      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
593
0
               sizeof(ifaddr_str));
594
0
      zlog_debug(
595
0
        "%s: local address %s (%u) lost querier election to %s (%u)",
596
0
        ifp->name, ifaddr_str,
597
0
        ntohl(igmp->ifaddr.s_addr), from_str,
598
0
        ntohl(from.s_addr));
599
0
    }
600
    /* Reset the other querier timer only if query is received from
601
     * the previously elected querier or a better new querier
602
     * This will make sure that non-querier elects the new querier
603
     * whose ip address is higher than the old querier
604
     * in case the old querier goes down via other querier present
605
     * timer expiry
606
     */
607
0
    if (ntohl(from.s_addr) <= ntohl(igmp->querier_addr.s_addr)) {
608
0
      igmp->querier_addr.s_addr = from.s_addr;
609
0
      pim_igmp_other_querier_timer_on(igmp);
610
0
    }
611
0
  }
612
613
  /* IGMP version 3 is the only one where we process the RXed query */
614
0
  if (query_version == 3) {
615
0
    igmp_v3_recv_query(igmp, from_str, igmp_msg);
616
0
  }
617
618
0
  return 0;
619
0
}
620
621
static void on_trace(const char *label, struct interface *ifp,
622
         struct in_addr from)
623
0
{
624
0
  if (PIM_DEBUG_GM_TRACE) {
625
0
    char from_str[INET_ADDRSTRLEN];
626
0
    pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
627
0
    zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
628
0
  }
629
0
}
630
631
static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
632
             const char *from_str, char *igmp_msg,
633
             int igmp_msg_len)
634
0
{
635
0
  struct interface *ifp = igmp->interface;
636
0
  struct gm_group *group;
637
0
  struct in_addr group_addr;
638
639
0
  on_trace(__func__, igmp->interface, from);
640
641
0
  if (igmp->mtrace_only)
642
0
    return 0;
643
644
0
  if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
645
0
    zlog_warn(
646
0
      "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
647
0
      from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
648
0
    return -1;
649
0
  }
650
651
0
  if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
652
0
    zlog_warn(
653
0
      "Recv IGMP report v1 from %s on %s with invalid checksum",
654
0
      from_str, ifp->name);
655
0
    return -1;
656
0
  }
657
658
  /* Collecting IGMP Rx stats */
659
0
  igmp->igmp_stats.report_v1++;
660
661
0
  if (PIM_DEBUG_GM_TRACE) {
662
0
    zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
663
0
  }
664
665
0
  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
666
667
0
  if (pim_is_group_filtered(ifp->info, &group_addr))
668
0
    return -1;
669
670
  /* non-existent group is created as INCLUDE {empty} */
671
0
  group = igmp_add_group_by_addr(igmp, group_addr);
672
0
  if (!group) {
673
0
    return -1;
674
0
  }
675
676
0
  group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
677
678
0
  return 0;
679
0
}
680
681
bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
682
0
{
683
0
  char *igmp_msg;
684
0
  int igmp_msg_len;
685
0
  int msg_type;
686
0
  size_t ip_hlen; /* ip header length in bytes */
687
688
0
  if (len < sizeof(*ip_hdr)) {
689
0
    zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
690
0
        sizeof(*ip_hdr));
691
0
    return false;
692
0
  }
693
694
0
  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
695
0
  *hlen = ip_hlen;
696
697
0
  if (ip_hlen > len) {
698
0
    zlog_warn(
699
0
      "IGMP packet header claims size %zu, but we only have %zu bytes",
700
0
      ip_hlen, len);
701
0
    return false;
702
0
  }
703
704
0
  igmp_msg = (char *)ip_hdr + ip_hlen;
705
0
  igmp_msg_len = len - ip_hlen;
706
0
  msg_type = *igmp_msg;
707
708
0
  if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
709
0
    zlog_warn("IGMP message size=%d shorter than minimum=%d",
710
0
        igmp_msg_len, PIM_IGMP_MIN_LEN);
711
0
    return false;
712
0
  }
713
714
0
  if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
715
0
      && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
716
0
    if (ip_hdr->ip_ttl != 1) {
717
0
      zlog_warn(
718
0
        "Recv IGMP packet with invalid ttl=%u, discarding the packet",
719
0
        ip_hdr->ip_ttl);
720
0
      return false;
721
0
    }
722
0
  }
723
724
0
  return true;
725
0
}
726
727
int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
728
0
{
729
0
  struct ip *ip_hdr = (struct ip *)buf;
730
0
  size_t ip_hlen; /* ip header length in bytes */
731
0
  char *igmp_msg;
732
0
  int igmp_msg_len;
733
0
  int msg_type;
734
0
  char from_str[INET_ADDRSTRLEN];
735
0
  char to_str[INET_ADDRSTRLEN];
736
737
0
  if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
738
0
    return -1;
739
740
0
  igmp_msg = buf + ip_hlen;
741
0
  igmp_msg_len = len - ip_hlen;
742
0
  msg_type = *igmp_msg;
743
744
0
  pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
745
0
  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
746
747
0
  if (PIM_DEBUG_GM_PACKETS) {
748
0
    zlog_debug(
749
0
      "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
750
0
      from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
751
0
      msg_type, igmp_msg_len);
752
0
  }
753
754
0
  switch (msg_type) {
755
0
  case PIM_IGMP_MEMBERSHIP_QUERY: {
756
0
    int max_resp_code = igmp_msg[1];
757
0
    int query_version;
758
759
    /*
760
      RFC 3376: 7.1. Query Version Distinctions
761
      IGMPv1 Query: length = 8 octets AND Max Resp Code field is
762
      zero
763
      IGMPv2 Query: length = 8 octets AND Max Resp Code field is
764
      non-zero
765
      IGMPv3 Query: length >= 12 octets
766
    */
767
768
0
    if (igmp_msg_len == 8) {
769
0
      query_version = max_resp_code ? 2 : 1;
770
0
    } else if (igmp_msg_len >= 12) {
771
0
      query_version = 3;
772
0
    } else {
773
0
      zlog_warn("Unknown IGMP query version");
774
0
      return -1;
775
0
    }
776
777
0
    return igmp_recv_query(igmp, query_version, max_resp_code,
778
0
               ip_hdr->ip_src, from_str, igmp_msg,
779
0
               igmp_msg_len);
780
0
  }
781
782
0
  case PIM_IGMP_V3_MEMBERSHIP_REPORT:
783
0
    return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
784
0
             igmp_msg, igmp_msg_len);
785
786
0
  case PIM_IGMP_V2_MEMBERSHIP_REPORT:
787
0
    return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
788
0
             igmp_msg, igmp_msg_len);
789
790
0
  case PIM_IGMP_V1_MEMBERSHIP_REPORT:
791
0
    return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
792
0
             igmp_msg, igmp_msg_len);
793
794
0
  case PIM_IGMP_V2_LEAVE_GROUP:
795
0
    return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
796
0
            igmp_msg_len);
797
798
0
  case PIM_IGMP_MTRACE_RESPONSE:
799
0
    return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
800
0
             from_str, igmp_msg,
801
0
             igmp_msg_len);
802
0
  case PIM_IGMP_MTRACE_QUERY_REQUEST:
803
0
    return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
804
0
            from_str, igmp_msg,
805
0
            igmp_msg_len);
806
0
  }
807
808
0
  zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
809
810
  /* Collecting IGMP Rx stats */
811
0
  igmp->igmp_stats.unsupported++;
812
813
0
  return -1;
814
0
}
815
816
void pim_igmp_general_query_on(struct gm_sock *igmp)
817
0
{
818
0
  struct pim_interface *pim_ifp;
819
0
  int startup_mode;
820
0
  int query_interval;
821
822
  /*
823
    Since this socket is starting as querier,
824
    there should not exist a timer for other-querier-present.
825
   */
826
0
  assert(!igmp->t_other_querier_timer);
827
0
  pim_ifp = igmp->interface->info;
828
0
  assert(pim_ifp);
829
830
  /*
831
    RFC 3376: 8.6. Startup Query Interval
832
833
    The Startup Query Interval is the interval between General Queries
834
    sent by a Querier on startup.  Default: 1/4 the Query Interval.
835
    The first one should be sent out immediately instead of 125/4
836
    seconds from now.
837
  */
838
0
  startup_mode = igmp->startup_query_count > 0;
839
0
  if (startup_mode) {
840
    /*
841
     * If this is the first time we are sending a query on a
842
     * newly configured igmp interface send it out in 1 second
843
     * just to give the entire world a tiny bit of time to settle
844
     * else the query interval is:
845
     * query_interval = pim_ifp->gm_default_query_interval >> 2;
846
     */
847
0
    if (igmp->startup_query_count ==
848
0
        igmp->querier_robustness_variable)
849
0
      query_interval = 1;
850
0
    else
851
0
      query_interval = PIM_IGMP_SQI(
852
0
        pim_ifp->gm_default_query_interval);
853
854
0
    --igmp->startup_query_count;
855
0
  } else {
856
0
    query_interval = igmp->querier_query_interval;
857
0
  }
858
859
0
  if (PIM_DEBUG_GM_TRACE) {
860
0
    char ifaddr_str[INET_ADDRSTRLEN];
861
0
    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
862
0
             sizeof(ifaddr_str));
863
0
    zlog_debug(
864
0
      "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
865
0
      ifaddr_str, query_interval,
866
0
      startup_mode ? "startup" : "non-startup", igmp->fd);
867
0
  }
868
0
  event_add_timer(router->master, pim_igmp_general_query, igmp,
869
0
      query_interval, &igmp->t_igmp_query_timer);
870
0
}
871
872
void pim_igmp_general_query_off(struct gm_sock *igmp)
873
0
{
874
0
  assert(igmp);
875
876
0
  if (PIM_DEBUG_GM_TRACE) {
877
0
    if (igmp->t_igmp_query_timer) {
878
0
      char ifaddr_str[INET_ADDRSTRLEN];
879
0
      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
880
0
               sizeof(ifaddr_str));
881
0
      zlog_debug(
882
0
        "IGMP querier %s fd=%d cancelling query TIMER event on %s",
883
0
        ifaddr_str, igmp->fd, igmp->interface->name);
884
0
    }
885
0
  }
886
0
  EVENT_OFF(igmp->t_igmp_query_timer);
887
0
}
888
889
/* Issue IGMP general query */
890
static void pim_igmp_general_query(struct event *t)
891
0
{
892
0
  struct gm_sock *igmp;
893
0
  struct in_addr dst_addr;
894
0
  struct in_addr group_addr;
895
0
  struct pim_interface *pim_ifp;
896
0
  int query_buf_size;
897
0
898
0
  igmp = EVENT_ARG(t);
899
0
900
0
  assert(igmp->interface);
901
0
  assert(igmp->interface->info);
902
0
903
0
  pim_ifp = igmp->interface->info;
904
0
905
0
  if (pim_ifp->igmp_version == 3) {
906
0
    query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
907
0
  } else {
908
0
    query_buf_size = IGMP_V12_MSG_SIZE;
909
0
  }
910
0
911
0
  char query_buf[query_buf_size];
912
0
913
0
  /*
914
0
    RFC3376: 4.1.12. IP Destination Addresses for Queries
915
0
916
0
    In IGMPv3, General Queries are sent with an IP destination address
917
0
    of 224.0.0.1, the all-systems multicast address.  Group-Specific
918
0
    and Group-and-Source-Specific Queries are sent with an IP
919
0
    destination address equal to the multicast address of interest.
920
0
  */
921
0
922
0
  dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
923
0
  group_addr.s_addr = PIM_NET_INADDR_ANY;
924
0
925
0
  if (PIM_DEBUG_GM_TRACE) {
926
0
    char querier_str[INET_ADDRSTRLEN];
927
0
    char dst_str[INET_ADDRSTRLEN];
928
0
    pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
929
0
             sizeof(querier_str));
930
0
    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
931
0
    zlog_debug("Querier %s issuing IGMP general query to %s on %s",
932
0
         querier_str, dst_str, igmp->interface->name);
933
0
  }
934
0
935
0
  igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
936
0
      sizeof(query_buf), 0 /* num_sources */, dst_addr,
937
0
      group_addr, pim_ifp->gm_query_max_response_time_dsec,
938
0
      1 /* s_flag: always set for general queries */, igmp);
939
0
940
0
  pim_igmp_general_query_on(igmp);
941
0
}
942
943
static void sock_close(struct gm_sock *igmp)
944
0
{
945
0
  pim_igmp_other_querier_timer_off(igmp);
946
0
  pim_igmp_general_query_off(igmp);
947
948
0
  if (PIM_DEBUG_GM_TRACE_DETAIL) {
949
0
    if (igmp->t_igmp_read) {
950
0
      zlog_debug(
951
0
        "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
952
0
        &igmp->ifaddr, igmp->fd,
953
0
        igmp->interface->name);
954
0
    }
955
0
  }
956
0
  EVENT_OFF(igmp->t_igmp_read);
957
958
0
  if (close(igmp->fd)) {
959
0
    flog_err(
960
0
      EC_LIB_SOCKET,
961
0
      "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
962
0
      &igmp->ifaddr, igmp->fd,
963
0
      igmp->interface->name, errno, safe_strerror(errno));
964
0
  }
965
966
0
  if (PIM_DEBUG_GM_TRACE_DETAIL) {
967
0
    zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
968
0
         &igmp->ifaddr, igmp->fd,
969
0
         igmp->interface->name);
970
0
  }
971
0
}
972
973
void igmp_startup_mode_on(struct gm_sock *igmp)
974
0
{
975
0
  struct pim_interface *pim_ifp;
976
977
0
  pim_ifp = igmp->interface->info;
978
979
  /*
980
    RFC 3376: 8.7. Startup Query Count
981
982
    The Startup Query Count is the number of Queries sent out on
983
    startup, separated by the Startup Query Interval.  Default: the
984
    Robustness Variable.
985
  */
986
0
  igmp->startup_query_count = igmp->querier_robustness_variable;
987
988
  /*
989
    Since we're (re)starting, reset QQI to default Query Interval
990
  */
991
0
  igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
992
0
}
993
994
static void igmp_group_free(struct gm_group *group)
995
0
{
996
0
  list_delete(&group->group_source_list);
997
998
0
  XFREE(MTYPE_PIM_IGMP_GROUP, group);
999
0
}
1000
1001
static void igmp_group_count_incr(struct pim_interface *pim_ifp)
1002
0
{
1003
0
  uint32_t group_count = listcount(pim_ifp->gm_group_list);
1004
1005
0
  ++pim_ifp->pim->gm_group_count;
1006
0
  if (pim_ifp->pim->gm_group_count == pim_ifp->pim->gm_watermark_limit) {
1007
0
    zlog_warn(
1008
0
      "IGMP group count reached watermark limit: %u(vrf: %s)",
1009
0
      pim_ifp->pim->gm_group_count,
1010
0
      VRF_LOGNAME(pim_ifp->pim->vrf));
1011
0
  }
1012
1013
0
  if (pim_ifp->igmp_peak_group_count < group_count)
1014
0
    pim_ifp->igmp_peak_group_count = group_count;
1015
0
}
1016
1017
static void igmp_group_count_decr(struct pim_interface *pim_ifp)
1018
0
{
1019
0
  if (pim_ifp->pim->gm_group_count == 0) {
1020
0
    zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1021
0
        VRF_LOGNAME(pim_ifp->pim->vrf));
1022
0
    return;
1023
0
  }
1024
1025
0
  --pim_ifp->pim->gm_group_count;
1026
0
}
1027
1028
void igmp_group_delete(struct gm_group *group)
1029
0
{
1030
0
  struct listnode *src_node;
1031
0
  struct listnode *src_nextnode;
1032
0
  struct gm_source *src;
1033
0
  struct pim_interface *pim_ifp = group->interface->info;
1034
1035
0
  if (PIM_DEBUG_GM_TRACE) {
1036
0
    char group_str[INET_ADDRSTRLEN];
1037
0
    pim_inet4_dump("<group?>", group->group_addr, group_str,
1038
0
             sizeof(group_str));
1039
0
    zlog_debug("Deleting IGMP group %s from interface %s",
1040
0
         group_str, group->interface->name);
1041
0
  }
1042
1043
0
  for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1044
0
             src)) {
1045
0
    igmp_source_delete(src);
1046
0
  }
1047
1048
0
  EVENT_OFF(group->t_group_query_retransmit_timer);
1049
1050
0
  group_timer_off(group);
1051
0
  igmp_group_count_decr(pim_ifp);
1052
0
  listnode_delete(pim_ifp->gm_group_list, group);
1053
0
  hash_release(pim_ifp->gm_group_hash, group);
1054
1055
0
  igmp_group_free(group);
1056
0
}
1057
1058
void igmp_group_delete_empty_include(struct gm_group *group)
1059
0
{
1060
0
  assert(!group->group_filtermode_isexcl);
1061
0
  assert(!listcount(group->group_source_list));
1062
1063
0
  igmp_group_delete(group);
1064
0
}
1065
1066
void igmp_sock_free(struct gm_sock *igmp)
1067
0
{
1068
0
  assert(!igmp->t_igmp_read);
1069
0
  assert(!igmp->t_igmp_query_timer);
1070
0
  assert(!igmp->t_other_querier_timer);
1071
1072
0
  XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1073
0
}
1074
1075
void igmp_sock_delete(struct gm_sock *igmp)
1076
0
{
1077
0
  struct pim_interface *pim_ifp;
1078
1079
0
  sock_close(igmp);
1080
1081
0
  pim_ifp = igmp->interface->info;
1082
1083
0
  listnode_delete(pim_ifp->gm_socket_list, igmp);
1084
1085
0
  igmp_sock_free(igmp);
1086
1087
0
  if (!listcount(pim_ifp->gm_socket_list))
1088
0
    pim_igmp_if_reset(pim_ifp);
1089
0
}
1090
1091
void igmp_sock_delete_all(struct interface *ifp)
1092
0
{
1093
0
  struct pim_interface *pim_ifp;
1094
0
  struct listnode *igmp_node, *igmp_nextnode;
1095
0
  struct gm_sock *igmp;
1096
1097
0
  pim_ifp = ifp->info;
1098
1099
0
  for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
1100
0
             igmp_nextnode, igmp)) {
1101
0
    igmp_sock_delete(igmp);
1102
0
  }
1103
0
}
1104
1105
static unsigned int igmp_group_hash_key(const void *arg)
1106
0
{
1107
0
  const struct gm_group *group = arg;
1108
1109
0
  return jhash_1word(group->group_addr.s_addr, 0);
1110
0
}
1111
1112
static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
1113
0
{
1114
0
  const struct gm_group *g1 = (const struct gm_group *)arg1;
1115
0
  const struct gm_group *g2 = (const struct gm_group *)arg2;
1116
1117
0
  if (g1->group_addr.s_addr == g2->group_addr.s_addr)
1118
0
    return true;
1119
1120
0
  return false;
1121
0
}
1122
1123
void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1124
2
{
1125
2
  char hash_name[64];
1126
1127
2
  pim_ifp->gm_socket_list = list_new();
1128
2
  pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
1129
1130
2
  pim_ifp->gm_group_list = list_new();
1131
2
  pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
1132
1133
2
  snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
1134
2
  pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1135
2
               igmp_group_hash_equal, hash_name);
1136
2
}
1137
1138
void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1139
0
{
1140
0
  struct listnode *grp_node, *grp_nextnode;
1141
0
  struct gm_group *grp;
1142
1143
0
  for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
1144
0
             grp)) {
1145
0
    igmp_group_delete(grp);
1146
0
  }
1147
0
}
1148
1149
void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1150
0
{
1151
0
  pim_igmp_if_reset(pim_ifp);
1152
1153
0
  assert(pim_ifp->gm_group_list);
1154
0
  assert(!listcount(pim_ifp->gm_group_list));
1155
1156
0
  list_delete(&pim_ifp->gm_group_list);
1157
0
  hash_free(pim_ifp->gm_group_hash);
1158
1159
0
  list_delete(&pim_ifp->gm_socket_list);
1160
0
}
1161
1162
static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1163
             struct interface *ifp, int mtrace_only)
1164
0
{
1165
0
  struct pim_interface *pim_ifp;
1166
0
  struct gm_sock *igmp;
1167
1168
0
  pim_ifp = ifp->info;
1169
1170
0
  if (PIM_DEBUG_GM_TRACE) {
1171
0
    zlog_debug(
1172
0
      "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1173
0
      fd, &ifaddr, ifp->name);
1174
0
  }
1175
1176
0
  igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1177
1178
0
  igmp->fd = fd;
1179
0
  igmp->interface = ifp;
1180
0
  igmp->ifaddr = ifaddr;
1181
0
  igmp->querier_addr = ifaddr;
1182
0
  igmp->t_igmp_read = NULL;
1183
0
  igmp->t_igmp_query_timer = NULL;
1184
0
  igmp->t_other_querier_timer = NULL; /* no other querier present */
1185
0
  igmp->querier_robustness_variable =
1186
0
    pim_ifp->gm_default_robustness_variable;
1187
0
  igmp->sock_creation = pim_time_monotonic_sec();
1188
1189
0
  igmp_stats_init(&igmp->igmp_stats);
1190
1191
0
  if (mtrace_only) {
1192
0
    igmp->mtrace_only = mtrace_only;
1193
0
    return igmp;
1194
0
  }
1195
1196
0
  igmp->mtrace_only = false;
1197
1198
  /*
1199
    igmp_startup_mode_on() will reset QQI:
1200
1201
    igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1202
  */
1203
0
  igmp_startup_mode_on(igmp);
1204
0
  pim_igmp_general_query_on(igmp);
1205
1206
0
  return igmp;
1207
0
}
1208
1209
static void igmp_read_on(struct gm_sock *igmp);
1210
1211
static void pim_igmp_read(struct event *t)
1212
0
{
1213
0
  uint8_t buf[10000];
1214
0
  struct gm_sock *igmp = (struct gm_sock *)EVENT_ARG(t);
1215
0
  struct sockaddr_storage from;
1216
0
  struct sockaddr_storage to;
1217
0
  socklen_t fromlen = sizeof(from);
1218
0
  socklen_t tolen = sizeof(to);
1219
0
  ifindex_t ifindex = -1;
1220
0
  int len;
1221
0
1222
0
  while (1) {
1223
0
    len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1224
0
              &fromlen, &to, &tolen, &ifindex);
1225
0
    if (len < 0) {
1226
0
      if (errno == EINTR)
1227
0
        continue;
1228
0
      if (errno == EWOULDBLOCK || errno == EAGAIN)
1229
0
        break;
1230
0
1231
0
      goto done;
1232
0
    }
1233
0
  }
1234
0
1235
0
done:
1236
0
  igmp_read_on(igmp);
1237
0
}
1238
1239
static void igmp_read_on(struct gm_sock *igmp)
1240
0
{
1241
1242
0
  if (PIM_DEBUG_GM_TRACE_DETAIL) {
1243
0
    zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1244
0
         igmp->fd);
1245
0
  }
1246
0
  event_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
1247
0
           &igmp->t_igmp_read);
1248
0
}
1249
1250
struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1251
          struct in_addr ifaddr, struct interface *ifp,
1252
          bool mtrace_only)
1253
1
{
1254
1
  struct gm_sock *igmp;
1255
1
  struct sockaddr_in sin;
1256
1
  int fd;
1257
1258
#ifndef FUZZING
1259
  fd = igmp_sock_open(ifaddr, ifp);
1260
  if (fd < 0) {
1261
    zlog_warn("Could not open IGMP socket for %pI4 on %s",
1262
        &ifaddr, ifp->name);
1263
    return NULL;
1264
  }
1265
#else
1266
1
  fd = 69;
1267
1
#endif
1268
1269
1
  sin.sin_family = AF_INET;
1270
1
  sin.sin_addr = ifaddr;
1271
1
  sin.sin_port = 0;
1272
1
  if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
1273
1
    zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1274
1
        &ifaddr, ifp->name, strerror(errno), errno);
1275
1
    close(fd);
1276
1277
1
    return NULL;
1278
1
  }
1279
1280
0
  igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
1281
1282
0
  igmp_read_on(igmp);
1283
1284
0
  listnode_add(igmp_sock_list, igmp);
1285
1286
#ifdef IGMP_SOCK_DUMP
1287
  igmp_sock_dump(igmp_sock_array);
1288
#endif
1289
1290
0
  return igmp;
1291
1
}
1292
1293
/*
1294
  RFC 3376: 6.5. Switching Router Filter-Modes
1295
1296
  When a router's filter-mode for a group is EXCLUDE and the group
1297
  timer expires, the router filter-mode for the group transitions to
1298
  INCLUDE.
1299
1300
  A router uses source records with running source timers as its state
1301
  for the switch to a filter-mode of INCLUDE.  If there are any source
1302
  records with source timers greater than zero (i.e., requested to be
1303
  forwarded), a router switches to filter-mode of INCLUDE using those
1304
  source records.  Source records whose timers are zero (from the
1305
  previous EXCLUDE mode) are deleted.
1306
 */
1307
static void igmp_group_timer(struct event *t)
1308
0
{
1309
0
  struct gm_group *group;
1310
0
1311
0
  group = EVENT_ARG(t);
1312
0
1313
0
  if (PIM_DEBUG_GM_TRACE) {
1314
0
    char group_str[INET_ADDRSTRLEN];
1315
0
    pim_inet4_dump("<group?>", group->group_addr, group_str,
1316
0
             sizeof(group_str));
1317
0
    zlog_debug("%s: Timer for group %s on interface %s", __func__,
1318
0
         group_str, group->interface->name);
1319
0
  }
1320
0
1321
0
  assert(group->group_filtermode_isexcl);
1322
0
1323
0
  group->group_filtermode_isexcl = 0;
1324
0
1325
0
  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1326
0
  igmp_anysource_forward_stop(group);
1327
0
1328
0
  igmp_source_delete_expired(group->group_source_list);
1329
0
1330
0
  assert(!group->group_filtermode_isexcl);
1331
0
1332
0
  /*
1333
0
    RFC 3376: 6.2.2. Definition of Group Timers
1334
0
1335
0
    If there are no more source records for the group, delete group
1336
0
    record.
1337
0
  */
1338
0
  if (listcount(group->group_source_list) < 1) {
1339
0
    igmp_group_delete_empty_include(group);
1340
0
  }
1341
0
}
1342
1343
static void group_timer_off(struct gm_group *group)
1344
0
{
1345
0
  if (!group->t_group_timer)
1346
0
    return;
1347
1348
0
  if (PIM_DEBUG_GM_TRACE) {
1349
0
    char group_str[INET_ADDRSTRLEN];
1350
0
    pim_inet4_dump("<group?>", group->group_addr, group_str,
1351
0
             sizeof(group_str));
1352
0
    zlog_debug("Cancelling TIMER event for group %s on %s",
1353
0
         group_str, group->interface->name);
1354
0
  }
1355
0
  EVENT_OFF(group->t_group_timer);
1356
0
}
1357
1358
void igmp_group_timer_on(struct gm_group *group, long interval_msec,
1359
       const char *ifname)
1360
0
{
1361
0
  group_timer_off(group);
1362
1363
0
  if (PIM_DEBUG_GM_EVENTS) {
1364
0
    char group_str[INET_ADDRSTRLEN];
1365
0
    pim_inet4_dump("<group?>", group->group_addr, group_str,
1366
0
             sizeof(group_str));
1367
0
    zlog_debug(
1368
0
      "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1369
0
      interval_msec / 1000, interval_msec % 1000, group_str,
1370
0
      ifname);
1371
0
  }
1372
1373
  /*
1374
    RFC 3376: 6.2.2. Definition of Group Timers
1375
1376
    The group timer is only used when a group is in EXCLUDE mode and
1377
    it represents the time for the *filter-mode* of the group to
1378
    expire and switch to INCLUDE mode.
1379
  */
1380
0
  assert(group->group_filtermode_isexcl);
1381
1382
0
  event_add_timer_msec(router->master, igmp_group_timer, group,
1383
0
           interval_msec, &group->t_group_timer);
1384
0
}
1385
1386
struct gm_group *find_group_by_addr(struct gm_sock *igmp,
1387
            struct in_addr group_addr)
1388
0
{
1389
0
  struct gm_group lookup;
1390
0
  struct pim_interface *pim_ifp = igmp->interface->info;
1391
1392
0
  lookup.group_addr.s_addr = group_addr.s_addr;
1393
1394
0
  return hash_lookup(pim_ifp->gm_group_hash, &lookup);
1395
0
}
1396
1397
struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
1398
          struct in_addr group_addr)
1399
0
{
1400
0
  struct gm_group *group;
1401
0
  struct pim_interface *pim_ifp = igmp->interface->info;
1402
1403
0
  group = find_group_by_addr(igmp, group_addr);
1404
0
  if (group) {
1405
0
    return group;
1406
0
  }
1407
1408
0
  if (!pim_is_group_224_4(group_addr)) {
1409
0
    zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1410
0
        __func__);
1411
0
    return NULL;
1412
0
  }
1413
1414
0
  if (pim_is_group_224_0_0_0_24(group_addr)) {
1415
0
    if (PIM_DEBUG_GM_TRACE)
1416
0
      zlog_debug(
1417
0
        "%s: Group specified %pI4 is part of 224.0.0.0/24",
1418
0
        __func__, &group_addr);
1419
0
    return NULL;
1420
0
  }
1421
  /*
1422
    Non-existant group is created as INCLUDE {empty}:
1423
1424
    RFC 3376 - 5.1. Action on Change of Interface State
1425
1426
    If no interface state existed for that multicast address before
1427
    the change (i.e., the change consisted of creating a new
1428
    per-interface record), or if no state exists after the change
1429
    (i.e., the change consisted of deleting a per-interface record),
1430
    then the "non-existent" state is considered to have a filter mode
1431
    of INCLUDE and an empty source list.
1432
  */
1433
1434
0
  group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1435
1436
0
  group->group_source_list = list_new();
1437
0
  group->group_source_list->del = (void (*)(void *))igmp_source_free;
1438
1439
0
  group->t_group_timer = NULL;
1440
0
  group->t_group_query_retransmit_timer = NULL;
1441
0
  group->group_specific_query_retransmit_count = 0;
1442
0
  group->group_addr = group_addr;
1443
0
  group->interface = igmp->interface;
1444
0
  group->last_igmp_v1_report_dsec = -1;
1445
0
  group->last_igmp_v2_report_dsec = -1;
1446
0
  group->group_creation = pim_time_monotonic_sec();
1447
0
  group->igmp_version = IGMP_DEFAULT_VERSION;
1448
1449
  /* initialize new group as INCLUDE {empty} */
1450
0
  group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1451
1452
0
  listnode_add(pim_ifp->gm_group_list, group);
1453
0
  group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
1454
1455
0
  if (PIM_DEBUG_GM_TRACE) {
1456
0
    char group_str[INET_ADDRSTRLEN];
1457
0
    pim_inet4_dump("<group?>", group->group_addr, group_str,
1458
0
             sizeof(group_str));
1459
0
    zlog_debug(
1460
0
      "Creating new IGMP group %s on socket %d interface %s",
1461
0
      group_str, igmp->fd, igmp->interface->name);
1462
0
  }
1463
1464
0
  igmp_group_count_incr(pim_ifp);
1465
1466
  /*
1467
    RFC 3376: 6.2.2. Definition of Group Timers
1468
1469
    The group timer is only used when a group is in EXCLUDE mode and
1470
    it represents the time for the *filter-mode* of the group to
1471
    expire and switch to INCLUDE mode.
1472
  */
1473
0
  assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1474
0
  assert(!group->t_group_timer);     /* group timer == 0 */
1475
1476
  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1477
0
  igmp_anysource_forward_stop(group);
1478
1479
0
  return group;
1480
0
}
1481
1482
void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
1483
         int query_buf_size, int num_sources,
1484
         struct in_addr dst_addr, struct in_addr group_addr,
1485
         int query_max_response_time_dsec, uint8_t s_flag,
1486
         struct gm_sock *igmp)
1487
0
{
1488
0
  if (pim_addr_is_any(group_addr) &&
1489
0
      ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
1490
0
    igmp->igmp_stats.general_queries_sent++;
1491
0
  else if (group)
1492
0
    igmp->igmp_stats.group_queries_sent++;
1493
1494
0
  if (igmp_version == 3) {
1495
0
    igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
1496
0
           query_buf, query_buf_size, num_sources,
1497
0
           dst_addr, group_addr,
1498
0
           query_max_response_time_dsec, s_flag,
1499
0
           igmp->querier_robustness_variable,
1500
0
           igmp->querier_query_interval);
1501
0
  } else if (igmp_version == 2) {
1502
0
    igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
1503
0
           query_buf, dst_addr, group_addr,
1504
0
           query_max_response_time_dsec);
1505
0
  }
1506
0
}
1507
1508
void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1509
0
{
1510
0
  struct pim_interface *pim_ifp = ifp->info;
1511
0
  struct listnode *sock_node = NULL;
1512
0
  struct gm_sock *igmp = NULL;
1513
0
  struct in_addr dst_addr;
1514
0
  struct in_addr group_addr;
1515
0
  int query_buf_size;
1516
1517
0
  if (!igmp_ver)
1518
0
    igmp_ver = 2;
1519
1520
0
  if (igmp_ver == 3)
1521
0
    query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1522
0
  else
1523
0
    query_buf_size = IGMP_V12_MSG_SIZE;
1524
1525
0
  dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1526
0
  group_addr.s_addr = PIM_NET_INADDR_ANY;
1527
1528
0
  if (PIM_DEBUG_GM_TRACE)
1529
0
    zlog_debug("Issuing general query on request on %s", ifp->name);
1530
1531
0
  for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
1532
1533
0
    char query_buf[query_buf_size];
1534
1535
0
    igmp_send_query(
1536
0
      igmp_ver, 0 /* igmp_group */, query_buf,
1537
0
      sizeof(query_buf), 0 /* num_sources */, dst_addr,
1538
0
      group_addr, pim_ifp->gm_query_max_response_time_dsec,
1539
0
      1 /* s_flag: always set for general queries */, igmp);
1540
0
  }
1541
0
}