Coverage Report

Created: 2025-08-03 06:36

/src/frr/pimd/pim_msg.c
Line
Count
Source (jump to first uncovered line)
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 "if.h"
10
#include "log.h"
11
#include "prefix.h"
12
#include "vty.h"
13
#include "plist.h"
14
15
#include "pimd.h"
16
#include "pim_instance.h"
17
#include "pim_vty.h"
18
#include "pim_pim.h"
19
#include "pim_msg.h"
20
#include "pim_util.h"
21
#include "pim_str.h"
22
#include "pim_iface.h"
23
#include "pim_rp.h"
24
#include "pim_rpf.h"
25
#include "pim_register.h"
26
#include "pim_jp_agg.h"
27
#include "pim_oil.h"
28
29
void pim_msg_build_header(pim_addr src, pim_addr dst, uint8_t *pim_msg,
30
        size_t pim_msg_size, uint8_t pim_msg_type,
31
        bool no_fwd)
32
1.09k
{
33
1.09k
  struct pim_msg_header *header = (struct pim_msg_header *)pim_msg;
34
1.09k
  struct iovec iov[2], *iovp = iov;
35
36
  /*
37
   * The checksum for Registers is done only on the first 8 bytes of the
38
   * packet, including the PIM header and the next 4 bytes, excluding the
39
   * data packet portion
40
   *
41
   * for IPv6, the pseudoheader upper-level protocol length is also
42
   * truncated, so let's just set it here before everything else.
43
   */
44
1.09k
  if (pim_msg_type == PIM_MSG_TYPE_REGISTER)
45
0
    pim_msg_size = PIM_MSG_REGISTER_LEN;
46
47
#if PIM_IPV == 6
48
  struct ipv6_ph phdr = {
49
    .src = src,
50
    .dst = dst,
51
    .ulpl = htonl(pim_msg_size),
52
    .next_hdr = IPPROTO_PIM,
53
  };
54
55
  iovp->iov_base = &phdr;
56
  iovp->iov_len = sizeof(phdr);
57
  iovp++;
58
#endif
59
60
  /*
61
   * Write header
62
   */
63
1.09k
  header->ver = PIM_PROTO_VERSION;
64
1.09k
  header->type = pim_msg_type;
65
1.09k
  header->Nbit = no_fwd;
66
1.09k
  header->reserved = 0;
67
68
1.09k
  header->checksum = 0;
69
1.09k
  iovp->iov_base = header;
70
1.09k
  iovp->iov_len = pim_msg_size;
71
1.09k
  iovp++;
72
73
1.09k
  header->checksum = in_cksumv(iov, iovp - iov);
74
1.09k
}
75
76
uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, struct in_addr addr)
77
0
{
78
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
79
0
  buf[1] = '\0';            /* native encoding */
80
0
  memcpy(buf + 2, &addr, sizeof(struct in_addr));
81
82
0
  return buf + PIM_ENCODED_IPV4_UCAST_SIZE;
83
0
}
84
85
uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, struct in_addr addr)
86
0
{
87
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
88
0
  buf[1] = '\0';            /* native encoding */
89
0
  buf[2] = '\0';            /* reserved */
90
0
  buf[3] = 32;            /* mask len */
91
0
  memcpy(buf + 4, &addr, sizeof(struct in_addr));
92
93
0
  return buf + PIM_ENCODED_IPV4_GROUP_SIZE;
94
0
}
95
96
uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, struct in_addr addr,
97
           uint8_t bits)
98
0
{
99
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */
100
0
  buf[1] = '\0';            /* native encoding */
101
0
  buf[2] = bits;
102
0
  buf[3] = 32; /* mask len */
103
0
  memcpy(buf + 4, &addr, sizeof(struct in_addr));
104
105
0
  return buf + PIM_ENCODED_IPV4_SOURCE_SIZE;
106
0
}
107
108
uint8_t *pim_msg_addr_encode_ipv6_source(uint8_t *buf, struct in6_addr addr,
109
           uint8_t bits)
110
0
{
111
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV6; /* addr family */
112
0
  buf[1] = '\0';            /* native encoding */
113
0
  buf[2] = bits;
114
0
  buf[3] = 128; /* mask len */
115
0
  buf += 4;
116
117
0
  memcpy(buf, &addr, sizeof(addr));
118
0
  buf += sizeof(addr);
119
120
0
  return buf;
121
0
}
122
123
uint8_t *pim_msg_addr_encode_ipv6_ucast(uint8_t *buf, struct in6_addr addr)
124
0
{
125
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV6; /* addr family */
126
0
  buf[1] = '\0';            /* native encoding */
127
0
  buf += 2;
128
129
0
  memcpy(buf, &addr, sizeof(addr));
130
0
  buf += sizeof(addr);
131
132
0
  return buf;
133
0
}
134
135
uint8_t *pim_msg_addr_encode_ipv6_group(uint8_t *buf, struct in6_addr addr)
136
0
{
137
0
  buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV6; /* addr family */
138
0
  buf[1] = '\0';            /* native encoding */
139
0
  buf[2] = '\0';            /* reserved */
140
0
  buf[3] = 128;           /* mask len */
141
0
  buf += 4;
142
143
0
  memcpy(buf, &addr, sizeof(addr));
144
0
  buf += sizeof(addr);
145
146
0
  return buf;
147
0
}
148
149
#if PIM_IPV == 4
150
0
#define pim_msg_addr_encode(what) pim_msg_addr_encode_ipv4_##what
151
#else
152
#define pim_msg_addr_encode(what) pim_msg_addr_encode_ipv6_##what
153
#endif
154
155
uint8_t *pim_msg_addr_encode_ucast(uint8_t *buf, pim_addr addr)
156
0
{
157
0
  return pim_msg_addr_encode(ucast)(buf, addr);
158
0
}
159
160
uint8_t *pim_msg_addr_encode_group(uint8_t *buf, pim_addr addr)
161
0
{
162
0
  return pim_msg_addr_encode(group)(buf, addr);
163
0
}
164
165
uint8_t *pim_msg_addr_encode_source(uint8_t *buf, pim_addr addr, uint8_t bits)
166
0
{
167
0
  return pim_msg_addr_encode(source)(buf, addr, bits);
168
0
}
169
170
/*
171
 * For the given 'struct pim_jp_sources' list
172
 * determine the size_t it would take up.
173
 */
174
size_t pim_msg_get_jp_group_size(struct list *sources)
175
0
{
176
0
  struct pim_jp_sources *js;
177
0
  size_t size = 0;
178
179
0
  if (!sources)
180
0
    return 0;
181
182
0
  size += sizeof(pim_encoded_group);
183
0
  size += 4; // Joined sources (2) + Pruned Sources (2)
184
185
0
  size += sizeof(pim_encoded_source) * sources->count;
186
187
0
  js = listgetdata(listhead(sources));
188
0
  if (js && pim_addr_is_any(js->up->sg.src) && js->is_join) {
189
0
    struct pim_upstream *child, *up;
190
0
    struct listnode *up_node;
191
192
0
    up = js->up;
193
0
    if (PIM_DEBUG_PIM_PACKETS)
194
0
      zlog_debug(
195
0
        "%s: Considering (%s) children for (S,G,rpt) prune",
196
0
        __func__, up->sg_str);
197
198
0
    for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
199
0
      if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) {
200
        /* If we are using SPT and the SPT and RPT IIFs
201
         * are different we can prune the source off
202
         * of the RPT.
203
         * If RPF_interface(S) is not resolved hold
204
         * decision to prune as SPT may end up on the
205
         * same IIF as RPF_interface(RP).
206
         */
207
0
        if (child->rpf.source_nexthop.interface &&
208
0
          !pim_rpf_is_same(&up->rpf,
209
0
            &child->rpf)) {
210
0
          size += sizeof(pim_encoded_source);
211
0
          PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
212
0
            child->flags);
213
0
          if (PIM_DEBUG_PIM_PACKETS)
214
0
            zlog_debug(
215
0
              "%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message",
216
0
              __func__, up->sg_str,
217
0
              child->sg_str);
218
0
        } else if (PIM_DEBUG_PIM_PACKETS)
219
0
          zlog_debug(
220
0
            "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)",
221
0
            __func__, up->sg_str,
222
0
            child->sg_str);
223
0
      } else if (pim_upstream_empty_inherited_olist(child)) {
224
        /* S is supposed to be forwarded along the RPT
225
         * but it's inherited OIL is empty. So just
226
         * prune it off.
227
         */
228
0
        size += sizeof(pim_encoded_source);
229
0
        PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
230
0
            child->flags);
231
0
        if (PIM_DEBUG_PIM_PACKETS)
232
0
          zlog_debug(
233
0
            "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message",
234
0
            __func__, child->sg_str);
235
0
      } else if (PIM_DEBUG_PIM_PACKETS)
236
0
        zlog_debug(
237
0
          "%s: Do not add Prune %s to compound message %s",
238
0
          __func__, child->sg_str, up->sg_str);
239
0
    }
240
0
  }
241
0
  return size;
242
0
}
243
244
size_t pim_msg_build_jp_groups(struct pim_jp_groups *grp,
245
             struct pim_jp_agg_group *sgs, size_t size)
246
0
{
247
0
  struct listnode *node, *nnode;
248
0
  struct pim_jp_sources *source;
249
0
  struct pim_upstream *up = NULL;
250
0
  pim_addr stosend;
251
0
  uint8_t bits;
252
0
  uint8_t tgroups = 0;
253
254
0
  memset(grp, 0, size);
255
0
  pim_msg_addr_encode_group((uint8_t *)&grp->g, sgs->group);
256
257
0
  for (ALL_LIST_ELEMENTS(sgs->sources, node, nnode, source)) {
258
    /* number of joined/pruned sources */
259
0
    if (source->is_join)
260
0
      grp->joins++;
261
0
    else
262
0
      grp->prunes++;
263
264
0
    if (pim_addr_is_any(source->up->sg.src)) {
265
0
      struct pim_instance *pim = source->up->channel_oil->pim;
266
0
      struct pim_rpf *rpf = pim_rp_g(pim, source->up->sg.grp);
267
0
      bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT
268
0
             | PIM_ENCODE_RPT_BIT;
269
0
      stosend = rpf->rpf_addr;
270
      /* Only Send SGRpt in case of *,G Join */
271
0
      if (source->is_join)
272
0
        up = source->up;
273
0
    } else {
274
0
      bits = PIM_ENCODE_SPARSE_BIT;
275
0
      stosend = source->up->sg.src;
276
0
    }
277
278
0
    pim_msg_addr_encode_source((uint8_t *)&grp->s[tgroups], stosend,
279
0
             bits);
280
0
    tgroups++;
281
0
  }
282
283
0
  if (up) {
284
0
    struct pim_upstream *child;
285
286
0
    for (ALL_LIST_ELEMENTS(up->sources, node, nnode, child)) {
287
0
      if (PIM_UPSTREAM_FLAG_TEST_SEND_SG_RPT_PRUNE(
288
0
            child->flags)) {
289
0
        pim_msg_addr_encode_source(
290
0
          (uint8_t *)&grp->s[tgroups],
291
0
          child->sg.src,
292
0
          PIM_ENCODE_SPARSE_BIT |
293
0
            PIM_ENCODE_RPT_BIT);
294
0
        tgroups++;
295
0
        PIM_UPSTREAM_FLAG_UNSET_SEND_SG_RPT_PRUNE(
296
0
          child->flags);
297
0
        grp->prunes++;
298
0
      }
299
0
    }
300
0
  }
301
302
0
  grp->joins = htons(grp->joins);
303
0
  grp->prunes = htons(grp->prunes);
304
305
0
  return size;
306
0
}