Coverage Report

Created: 2026-09-08 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_join.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
4
/*
5
 * PIM for Quagga
6
 * Copyright (C) 2008  Everton da Silva Marques
7
 */
8
9
#include <zebra.h>
10
11
#include "log.h"
12
#include "prefix.h"
13
#include "if.h"
14
#include "vty.h"
15
#include "plist.h"
16
17
#include "pimd.h"
18
#include "pim_instance.h"
19
#include "pim_str.h"
20
#include "pim_tlv.h"
21
#include "pim_msg.h"
22
#include "pim_pim.h"
23
#include "pim_join.h"
24
#include "pim_oil.h"
25
#include "pim_iface.h"
26
#include "pim_hello.h"
27
#include "pim_ifchannel.h"
28
#include "pim_rpf.h"
29
#include "pim_rp.h"
30
#include "pim_jp_agg.h"
31
#include "pim_util.h"
32
#include "pim_ssm.h"
33
34
static void on_trace(const char *label, struct interface *ifp, pim_addr src)
35
0
{
36
0
  if (PIM_DEBUG_PIM_TRACE)
37
0
    zlog_debug("%s: from %pPA on %s", label, &src, ifp->name);
38
0
}
39
40
static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
41
          uint16_t holdtime, pim_addr upstream, pim_sgaddr *sg,
42
          uint8_t source_flags)
43
6.56k
{
44
6.56k
  struct pim_interface *pim_ifp = NULL;
45
46
6.56k
  if (PIM_DEBUG_PIM_TRACE)
47
0
    zlog_debug(
48
6.56k
      "%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
49
6.56k
      __func__, sg, !!(source_flags & PIM_RPT_BIT_MASK),
50
6.56k
      !!(source_flags & PIM_WILDCARD_BIT_MASK), &upstream,
51
6.56k
      holdtime, &neigh->source_addr, ifp->name);
52
53
6.56k
  pim_ifp = ifp->info;
54
6.56k
  assert(pim_ifp);
55
56
6.56k
  ++pim_ifp->pim_ifstat_join_recv;
57
58
  /*
59
   * If the RPT and WC are set it's a (*,G)
60
   * and the source is the RP
61
   */
62
6.56k
  if (CHECK_FLAG(source_flags, PIM_WILDCARD_BIT_MASK)) {
63
    /* As per RFC 7761 Section 4.9.1:
64
     * The RPT (or Rendezvous Point Tree) bit is a 1-bit value for
65
     * use with PIM Join/Prune messages (see Section 4.9.5.1). If
66
     * the WC bit is 1, the RPT bit MUST be 1.
67
     */
68
998
    if (!CHECK_FLAG(source_flags, PIM_RPT_BIT_MASK)) {
69
282
      if (PIM_DEBUG_PIM_J_P)
70
0
        zlog_debug(
71
282
          "Discarding (*,G)=%pSG join since WC bit is set but RPT bit is unset",
72
282
          sg);
73
74
282
      return;
75
282
    }
76
77
716
    struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp);
78
716
    pim_addr rpf_addr;
79
80
716
    if (!rp) {
81
244
      zlog_warn("%s: Lookup of RP failed for %pSG", __func__,
82
244
          sg);
83
244
      return;
84
244
    }
85
    /*
86
     * If the RP sent in the message is not
87
     * our RP for the group, drop the message
88
     */
89
472
    rpf_addr = rp->rpf_addr;
90
472
    if (pim_addr_cmp(sg->src, rpf_addr)) {
91
466
      zlog_warn(
92
466
        "%s: Specified RP(%pPAs) in join is different than our configured RP(%pPAs)",
93
466
        __func__, &sg->src, &rpf_addr);
94
466
      return;
95
466
    }
96
97
6
    if (pim_is_grp_ssm(pim_ifp->pim, sg->grp)) {
98
1
      zlog_warn(
99
1
        "%s: Specified Group(%pPA) in join is now in SSM, not allowed to create PIM state",
100
1
        __func__, &sg->grp);
101
1
      return;
102
1
    }
103
104
5
    sg->src = PIMADDR_ANY;
105
5
  }
106
107
  /* Restart join expiry timer */
108
5.57k
  pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, sg,
109
5.57k
             source_flags, holdtime);
110
5.57k
}
111
112
static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh,
113
           uint16_t holdtime, pim_addr upstream, pim_sgaddr *sg,
114
           uint8_t source_flags)
115
45.5k
{
116
45.5k
  struct pim_interface *pim_ifp = NULL;
117
118
45.5k
  if (PIM_DEBUG_PIM_TRACE)
119
0
    zlog_debug(
120
45.5k
      "%s: prune (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
121
45.5k
      __func__, sg, source_flags & PIM_RPT_BIT_MASK,
122
45.5k
      source_flags & PIM_WILDCARD_BIT_MASK, &upstream,
123
45.5k
      holdtime, &neigh->source_addr, ifp->name);
124
125
45.5k
  pim_ifp = ifp->info;
126
45.5k
  assert(pim_ifp);
127
128
45.5k
  ++pim_ifp->pim_ifstat_prune_recv;
129
130
45.5k
  if (CHECK_FLAG(source_flags, PIM_WILDCARD_BIT_MASK)) {
131
    /* As per RFC 7761 Section 4.9.1:
132
     * The RPT (or Rendezvous Point Tree) bit is a 1-bit value for
133
     * use with PIM Join/Prune messages (see Section 4.9.5.1). If
134
     * the WC bit is 1, the RPT bit MUST be 1.
135
     */
136
7.65k
    if (!CHECK_FLAG(source_flags, PIM_RPT_BIT_MASK)) {
137
345
      if (PIM_DEBUG_PIM_J_P)
138
0
        zlog_debug(
139
345
          "Discarding (*,G)=%pSG prune since WC bit is set but RPT bit is unset",
140
345
          sg);
141
142
345
      return;
143
345
    }
144
145
    /*
146
     * RFC 4601 Section 4.5.2:
147
     * Received Prune(*,G) messages are processed even if the
148
     * RP in the message does not match RP(G).
149
     */
150
7.30k
    if (PIM_DEBUG_PIM_TRACE)
151
0
      zlog_debug("%s: Prune received with RP(%pPAs) for %pSG",
152
7.30k
           __func__, &sg->src, sg);
153
154
7.30k
    sg->src = PIMADDR_ANY;
155
7.30k
  }
156
157
45.1k
  pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime);
158
45.1k
}
159
160
int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
161
           pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size)
162
754
{
163
754
  pim_addr msg_upstream_addr;
164
754
  bool wrong_af = false;
165
754
  struct pim_interface *pim_ifp;
166
754
  uint8_t msg_num_groups;
167
754
  uint16_t msg_holdtime;
168
754
  int addr_offset;
169
754
  uint8_t *buf;
170
754
  uint8_t *pastend;
171
754
  int remain;
172
754
  int group;
173
754
  struct pim_ifchannel *child = NULL;
174
754
  struct listnode *ch_node, *nch_node;
175
176
754
  buf = tlv_buf;
177
754
  pastend = tlv_buf + tlv_buf_size;
178
754
  pim_ifp = ifp->info;
179
180
754
  if (pim_ifp->pim_passive_enable) {
181
0
    if (PIM_DEBUG_PIM_PACKETS)
182
0
      zlog_debug(
183
0
        "skip receiving PIM message on passive interface %s",
184
0
        ifp->name);
185
0
    return 0;
186
0
  }
187
188
  /*
189
    Parse ucast addr
190
  */
191
754
  addr_offset = pim_parse_addr_ucast(&msg_upstream_addr, buf,
192
754
             pastend - buf, &wrong_af);
193
754
  if (addr_offset < 1) {
194
26
    zlog_warn("%s: pim_parse_addr_ucast() failure: from %pPA on %s",
195
26
        __func__, &src_addr, ifp->name);
196
26
    return -1;
197
26
  }
198
728
  buf += addr_offset;
199
200
  /*
201
    Check upstream address family
202
   */
203
728
  if (wrong_af) {
204
0
    zlog_warn(
205
0
      "%s: ignoring join/prune directed to unexpected addr family from %pPA on %s",
206
0
      __func__, &src_addr, ifp->name);
207
0
    return -2;
208
0
  }
209
210
728
  remain = pastend - buf;
211
728
  if (remain < 4) {
212
3
    zlog_warn(
213
3
      "%s: short join/prune message buffer for group list: size=%d minimum=%d from %pPA on %s",
214
3
      __func__, remain, 4, &src_addr, ifp->name);
215
3
    return -4;
216
3
  }
217
218
725
  ++buf; /* skip reserved byte */
219
725
  msg_num_groups = *(const uint8_t *)buf;
220
725
  ++buf;
221
725
  msg_holdtime = ntohs(*(const uint16_t *)buf);
222
725
  ++buf;
223
725
  ++buf;
224
225
725
  if (PIM_DEBUG_PIM_J_P)
226
0
    zlog_debug(
227
725
      "%s: join/prune upstream=%pPAs groups=%d holdtime=%d from %pPA on %s",
228
725
      __func__, &msg_upstream_addr, msg_num_groups,
229
725
      msg_holdtime, &src_addr, ifp->name);
230
231
  /* Scan groups */
232
5.72k
  for (group = 0; group < msg_num_groups; ++group) {
233
5.70k
    pim_sgaddr sg;
234
5.70k
    uint8_t msg_source_flags;
235
5.70k
    uint16_t msg_num_joined_sources;
236
5.70k
    uint16_t msg_num_pruned_sources;
237
5.70k
    int source;
238
5.70k
    struct pim_ifchannel *starg_ch = NULL, *sg_ch = NULL;
239
5.70k
    bool filtered = false;
240
241
5.70k
    memset(&sg, 0, sizeof(sg));
242
5.70k
    addr_offset = pim_parse_addr_group(&sg, buf, pastend - buf);
243
5.70k
    if (addr_offset < 1) {
244
99
      return -5;
245
99
    }
246
5.61k
    buf += addr_offset;
247
248
5.61k
    remain = pastend - buf;
249
5.61k
    if (remain < 4) {
250
11
      zlog_warn(
251
11
        "%s: short join/prune buffer for source list: size=%d minimum=%d from %pPA on %s",
252
11
        __func__, remain, 4, &src_addr, ifp->name);
253
11
      return -6;
254
11
    }
255
256
5.59k
    msg_num_joined_sources = ntohs(*(const uint16_t *)buf);
257
5.59k
    buf += 2;
258
5.59k
    msg_num_pruned_sources = ntohs(*(const uint16_t *)buf);
259
5.59k
    buf += 2;
260
261
5.59k
    if (PIM_DEBUG_PIM_J_P)
262
0
      zlog_debug(
263
5.59k
        "%s: join/prune upstream=%pPAs group=%pPA/32 join_src=%d prune_src=%d from %pPA on %s",
264
5.59k
        __func__, &msg_upstream_addr, &sg.grp,
265
5.59k
        msg_num_joined_sources, msg_num_pruned_sources,
266
5.59k
        &src_addr, ifp->name);
267
268
    /* boundary check */
269
5.59k
    filtered = pim_is_group_filtered(pim_ifp, &sg.grp);
270
271
    /* Scan joined sources */
272
12.1k
    for (source = 0; source < msg_num_joined_sources; ++source) {
273
6.71k
      addr_offset = pim_parse_addr_source(
274
6.71k
        &sg, &msg_source_flags, buf, pastend - buf);
275
6.71k
      if (addr_offset < 1) {
276
144
        return -7;
277
144
      }
278
279
6.56k
      buf += addr_offset;
280
281
      /* if we are filtering this group, skip the join */
282
6.56k
      if (filtered)
283
0
        continue;
284
285
6.56k
      recv_join(ifp, neigh, msg_holdtime, msg_upstream_addr,
286
6.56k
          &sg, msg_source_flags);
287
288
6.56k
      if (pim_addr_is_any(sg.src)) {
289
3.77k
        starg_ch = pim_ifchannel_find(ifp, &sg);
290
3.77k
        if (starg_ch)
291
3.24k
          pim_ifchannel_set_star_g_join_state(
292
3.24k
            starg_ch, 0, 1);
293
3.77k
      }
294
6.56k
    }
295
296
    /* Scan pruned sources */
297
50.9k
    for (source = 0; source < msg_num_pruned_sources; ++source) {
298
45.9k
      addr_offset = pim_parse_addr_source(
299
45.9k
        &sg, &msg_source_flags, buf, pastend - buf);
300
45.9k
      if (addr_offset < 1) {
301
460
        return -8;
302
460
      }
303
304
45.5k
      buf += addr_offset;
305
306
      /* if we are filtering this group, skip the prune */
307
45.5k
      if (filtered)
308
0
        continue;
309
310
45.5k
      recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr,
311
45.5k
           &sg, msg_source_flags);
312
      /*
313
       * So if we are receiving a S,G,RPT prune
314
       * before we have any data for that S,G
315
       * We need to retrieve the sg_ch after
316
       * we parse the prune.
317
       */
318
45.5k
      sg_ch = pim_ifchannel_find(ifp, &sg);
319
320
45.5k
      if (!sg_ch)
321
973
        continue;
322
323
      /* (*,G) prune received */
324
44.5k
      for (ALL_LIST_ELEMENTS(sg_ch->sources, ch_node,
325
44.5k
                 nch_node, child)) {
326
42.0k
        if (PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) {
327
31.8k
          if (child->ifjoin_state
328
31.8k
              == PIM_IFJOIN_PRUNE_PENDING_TMP)
329
1.05k
            EVENT_OFF(
330
31.8k
              child->t_ifjoin_prune_pending_timer);
331
31.8k
          EVENT_OFF(child->t_ifjoin_expiry_timer);
332
31.8k
          PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
333
31.8k
          child->ifjoin_state = PIM_IFJOIN_NOINFO;
334
31.8k
          delete_on_noinfo(child);
335
31.8k
        }
336
42.0k
      }
337
338
      /* Received SG-RPT Prune delete oif from specific S,G */
339
44.5k
      if (starg_ch && (msg_source_flags & PIM_RPT_BIT_MASK)
340
7.54k
          && !(msg_source_flags & PIM_WILDCARD_BIT_MASK)) {
341
5.53k
        struct pim_upstream *up = sg_ch->upstream;
342
5.53k
        PIM_IF_FLAG_SET_S_G_RPT(sg_ch->flags);
343
5.53k
        if (up) {
344
5.53k
          if (PIM_DEBUG_PIM_TRACE)
345
0
            zlog_debug(
346
5.53k
              "%s: SGRpt flag is set, del inherit oif from up %s",
347
5.53k
              __func__, up->sg_str);
348
5.53k
          pim_channel_del_inherited_oif(
349
5.53k
            up->channel_oil,
350
5.53k
            starg_ch->interface,
351
5.53k
            __func__);
352
5.53k
        }
353
5.53k
      }
354
44.5k
    }
355
4.99k
    if (starg_ch && !filtered)
356
2.73k
      pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0);
357
4.99k
    starg_ch = NULL;
358
4.99k
  } /* scan groups */
359
360
11
  return 0;
361
725
}
362
363
/*
364
 * J/P Message Format
365
 *
366
 * While the RFC clearly states that this is 32 bits wide, it
367
 * is cheating.  These fields:
368
 * Encoded-Unicast format   (6 bytes MIN)
369
 * Encoded-Group format     (8 bytes MIN)
370
 * Encoded-Source format    (8 bytes MIN)
371
 * are *not* 32 bits wide.
372
 *
373
 * Nor does the RFC explicitly call out the size for:
374
 * Reserved                 (1 byte)
375
 * Num Groups               (1 byte)
376
 * Holdtime                 (2 bytes)
377
 * Number of Joined Sources (2 bytes)
378
 * Number of Pruned Sources (2 bytes)
379
 *
380
 * This leads to a missleading representation from casual
381
 * reading and making assumptions.  Be careful!
382
 *
383
 *   0                   1                   2                   3
384
 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
385
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386
 *  |PIM Ver| Type  |   Reserved    |           Checksum            |
387
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388
 *  |        Upstream Neighbor Address (Encoded-Unicast format)     |
389
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390
 *  |  Reserved     | Num groups    |          Holdtime             |
391
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392
 *  |         Multicast Group Address 1 (Encoded-Group format)      |
393
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394
 *  |   Number of Joined Sources    |   Number of Pruned Sources    |
395
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396
 *  |        Joined Source Address 1 (Encoded-Source format)        |
397
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398
 *  |                             .                                 |
399
 *  |                             .                                 |
400
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401
 *  |        Joined Source Address n (Encoded-Source format)        |
402
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403
 *  |        Pruned Source Address 1 (Encoded-Source format)        |
404
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405
 *  |                             .                                 |
406
 *  |                             .                                 |
407
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408
 *  |        Pruned Source Address n (Encoded-Source format)        |
409
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410
 *  |         Multicast Group Address m (Encoded-Group format)      |
411
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
412
 *  |   Number of Joined Sources    |   Number of Pruned Sources    |
413
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
414
 *  |        Joined Source Address 1 (Encoded-Source format)        |
415
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416
 *  |                             .                                 |
417
 *  |                             .                                 |
418
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419
 *  |        Joined Source Address n (Encoded-Source format)        |
420
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421
 *  |        Pruned Source Address 1 (Encoded-Source format)        |
422
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423
 *  |                             .                                 |
424
 *  |                             .                                 |
425
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426
 *  |        Pruned Source Address n (Encoded-Source format)        |
427
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428
 */
429
int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
430
0
{
431
0
  struct pim_jp_agg_group *group;
432
0
  struct pim_interface *pim_ifp = NULL;
433
0
  struct pim_jp_groups *grp = NULL;
434
0
  struct pim_jp *msg = NULL;
435
0
  struct listnode *node, *nnode;
436
0
  uint8_t pim_msg[10000];
437
0
  uint8_t *curr_ptr = pim_msg;
438
0
  bool new_packet = true;
439
0
  size_t packet_left = 0;
440
0
  size_t packet_size = 0;
441
0
  size_t group_size = 0;
442
443
0
  if (rpf->source_nexthop.interface)
444
0
    pim_ifp = rpf->source_nexthop.interface->info;
445
0
  else {
446
0
    zlog_warn("%s: RPF interface is not present", __func__);
447
0
    return -1;
448
0
  }
449
450
451
0
  on_trace(__func__, rpf->source_nexthop.interface, rpf->rpf_addr);
452
453
0
  if (!pim_ifp) {
454
0
    zlog_warn("%s: multicast not enabled on interface %s", __func__,
455
0
        rpf->source_nexthop.interface->name);
456
0
    return -1;
457
0
  }
458
459
0
  if (pim_addr_is_any(rpf->rpf_addr)) {
460
0
    if (PIM_DEBUG_PIM_J_P)
461
0
      zlog_debug(
462
0
        "%s: upstream=%pPA is myself on interface %s",
463
0
        __func__, &rpf->rpf_addr,
464
0
        rpf->source_nexthop.interface->name);
465
0
    return 0;
466
0
  }
467
468
  /*
469
    RFC 4601: 4.3.1.  Sending Hello Messages
470
471
    Thus, if a router needs to send a Join/Prune or Assert message on
472
    an interface on which it has not yet sent a Hello message with the
473
    currently configured IP address, then it MUST immediately send the
474
    relevant Hello message without waiting for the Hello Timer to
475
    expire, followed by the Join/Prune or Assert message.
476
  */
477
0
  pim_hello_require(rpf->source_nexthop.interface);
478
479
0
  for (ALL_LIST_ELEMENTS(groups, node, nnode, group)) {
480
0
    if (new_packet) {
481
0
      msg = (struct pim_jp *)pim_msg;
482
483
0
      memset(msg, 0, sizeof(*msg));
484
485
0
      pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
486
0
              rpf->rpf_addr);
487
0
      msg->reserved = 0;
488
0
      msg->holdtime = htons(PIM_JP_HOLDTIME);
489
490
0
      new_packet = false;
491
492
0
      grp = &msg->groups[0];
493
0
      curr_ptr = (uint8_t *)grp;
494
0
      packet_size = sizeof(struct pim_msg_header);
495
0
      packet_size += sizeof(pim_encoded_unicast);
496
0
      packet_size +=
497
0
        4; // reserved (1) + groups (1) + holdtime (2)
498
499
0
      packet_left = rpf->source_nexthop.interface->mtu - 24;
500
0
      packet_left -= packet_size;
501
0
    }
502
0
    if (PIM_DEBUG_PIM_J_P)
503
0
      zlog_debug(
504
0
        "%s: sending (G)=%pPAs to upstream=%pPA on interface %s",
505
0
        __func__, &group->group, &rpf->rpf_addr,
506
0
        rpf->source_nexthop.interface->name);
507
508
0
    group_size = pim_msg_get_jp_group_size(group->sources);
509
0
    if (group_size > packet_left) {
510
0
      pim_msg_build_header(pim_ifp->primary_address,
511
0
               qpim_all_pim_routers_addr, pim_msg,
512
0
               packet_size,
513
0
               PIM_MSG_TYPE_JOIN_PRUNE, false);
514
0
      if (pim_msg_send(pim_ifp->pim_sock_fd,
515
0
           pim_ifp->primary_address,
516
0
           qpim_all_pim_routers_addr, pim_msg,
517
0
           packet_size,
518
0
           rpf->source_nexthop.interface)) {
519
0
        zlog_warn(
520
0
          "%s: could not send PIM message on interface %s",
521
0
          __func__,
522
0
          rpf->source_nexthop.interface->name);
523
0
      }
524
525
0
      msg = (struct pim_jp *)pim_msg;
526
0
      memset(msg, 0, sizeof(*msg));
527
528
0
      pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
529
0
              rpf->rpf_addr);
530
0
      msg->reserved = 0;
531
0
      msg->holdtime = htons(PIM_JP_HOLDTIME);
532
533
0
      new_packet = false;
534
535
0
      grp = &msg->groups[0];
536
0
      curr_ptr = (uint8_t *)grp;
537
0
      packet_size = sizeof(struct pim_msg_header);
538
0
      packet_size += sizeof(pim_encoded_unicast);
539
0
      packet_size +=
540
0
        4; // reserved (1) + groups (1) + holdtime (2)
541
542
0
      packet_left = rpf->source_nexthop.interface->mtu - 24;
543
0
      packet_left -= packet_size;
544
0
    }
545
546
0
    msg->num_groups++;
547
    /*
548
      Build PIM message
549
    */
550
551
0
    curr_ptr += group_size;
552
0
    packet_left -= group_size;
553
0
    packet_size += group_size;
554
0
    pim_msg_build_jp_groups(grp, group, group_size);
555
556
0
    if (!pim_ifp->pim_passive_enable) {
557
0
      pim_ifp->pim_ifstat_join_send += ntohs(grp->joins);
558
0
      pim_ifp->pim_ifstat_prune_send += ntohs(grp->prunes);
559
0
    }
560
561
0
    if (PIM_DEBUG_PIM_TRACE)
562
0
      zlog_debug(
563
0
        "%s: interface %s num_joins %u num_prunes %u",
564
0
        __func__, rpf->source_nexthop.interface->name,
565
0
        ntohs(grp->joins), ntohs(grp->prunes));
566
567
0
    grp = (struct pim_jp_groups *)curr_ptr;
568
0
    if (packet_left < sizeof(struct pim_jp_groups)
569
0
        || msg->num_groups == 255) {
570
0
      pim_msg_build_header(pim_ifp->primary_address,
571
0
               qpim_all_pim_routers_addr, pim_msg,
572
0
               packet_size,
573
0
               PIM_MSG_TYPE_JOIN_PRUNE, false);
574
0
      if (pim_msg_send(pim_ifp->pim_sock_fd,
575
0
           pim_ifp->primary_address,
576
0
           qpim_all_pim_routers_addr, pim_msg,
577
0
           packet_size,
578
0
           rpf->source_nexthop.interface)) {
579
0
        zlog_warn(
580
0
          "%s: could not send PIM message on interface %s",
581
0
          __func__,
582
0
          rpf->source_nexthop.interface->name);
583
0
      }
584
585
0
      new_packet = true;
586
0
    }
587
0
  }
588
589
590
0
  if (!new_packet) {
591
    // msg->num_groups = htons (msg->num_groups);
592
0
    pim_msg_build_header(
593
0
      pim_ifp->primary_address, qpim_all_pim_routers_addr,
594
0
      pim_msg, packet_size, PIM_MSG_TYPE_JOIN_PRUNE, false);
595
0
    if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
596
0
         qpim_all_pim_routers_addr, pim_msg,
597
0
         packet_size, rpf->source_nexthop.interface)) {
598
0
      zlog_warn(
599
0
        "%s: could not send PIM message on interface %s",
600
0
        __func__, rpf->source_nexthop.interface->name);
601
0
    }
602
0
  }
603
0
  return 0;
604
0
}