Coverage Report

Created: 2026-06-06 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_hello.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 "log.h"
10
#include "if.h"
11
12
#include "pimd.h"
13
#include "pim_instance.h"
14
#include "pim_pim.h"
15
#include "pim_str.h"
16
#include "pim_tlv.h"
17
#include "pim_util.h"
18
#include "pim_hello.h"
19
#include "pim_iface.h"
20
#include "pim_neighbor.h"
21
#include "pim_upstream.h"
22
#include "pim_bsm.h"
23
24
static void on_trace(const char *label, struct interface *ifp, pim_addr src)
25
0
{
26
0
  if (PIM_DEBUG_PIM_TRACE)
27
0
    zlog_debug("%s: from %pPAs on %s", label, &src, ifp->name);
28
0
}
29
30
static void tlv_trace_bool(const char *label, const char *tlv_name,
31
         const char *ifname, pim_addr src_addr, int isset,
32
         int value)
33
0
{
34
0
  if (isset)
35
0
    zlog_debug(
36
0
      "%s: PIM hello option from %pPAs on interface %s: %s=%d",
37
0
      label, &src_addr, ifname, tlv_name, value);
38
0
}
39
40
static void tlv_trace_uint16(const char *label, const char *tlv_name,
41
           const char *ifname, pim_addr src_addr, int isset,
42
           uint16_t value)
43
0
{
44
0
  if (isset)
45
0
    zlog_debug(
46
0
      "%s: PIM hello option from %pPAs on interface %s: %s=%u",
47
0
      label, &src_addr, ifname, tlv_name, value);
48
0
}
49
50
static void tlv_trace_uint32(const char *label, const char *tlv_name,
51
           const char *ifname, pim_addr src_addr, int isset,
52
           uint32_t value)
53
0
{
54
0
  if (isset)
55
0
    zlog_debug(
56
0
      "%s: PIM hello option from %pPAs on interface %s: %s=%u",
57
0
      label, &src_addr, ifname, tlv_name, value);
58
0
}
59
60
static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
61
         const char *ifname, pim_addr src_addr,
62
         int isset, uint32_t value)
63
0
{
64
0
  if (isset)
65
0
    zlog_debug(
66
0
      "%s: PIM hello option from %pPAs on interface %s: %s=%08x",
67
0
      label, &src_addr, ifname, tlv_name, value);
68
0
}
69
70
static void tlv_trace_list(const char *label, const char *tlv_name,
71
         const char *ifname, pim_addr src_addr, int isset,
72
         struct list *addr_list)
73
0
{
74
0
  if (isset)
75
0
    zlog_debug(
76
0
      "%s: PIM hello option from %pPAs on interface %s: %s size=%d list=%p",
77
0
      label, &src_addr, ifname, tlv_name,
78
0
      addr_list ? ((int)listcount(addr_list)) : -1,
79
0
      (void *)addr_list);
80
0
}
81
82
#define FREE_ADDR_LIST                                                         \
83
0
  if (hello_option_addr_list) {                                          \
84
0
    list_delete(&hello_option_addr_list);                          \
85
0
  }
86
87
#define FREE_ADDR_LIST_THEN_RETURN(code)                                       \
88
0
  {                                                                      \
89
0
    FREE_ADDR_LIST                                                 \
90
0
    return (code);                                                 \
91
0
  }
92
93
int pim_hello_recv(struct interface *ifp, pim_addr src_addr, uint8_t *tlv_buf,
94
       int tlv_buf_size)
95
0
{
96
0
  struct pim_interface *pim_ifp;
97
0
  struct pim_neighbor *neigh;
98
0
  uint8_t *tlv_curr;
99
0
  uint8_t *tlv_pastend;
100
0
  pim_hello_options hello_options =
101
0
    0; /* bit array recording options found */
102
0
  uint16_t hello_option_holdtime = 0;
103
0
  uint16_t hello_option_propagation_delay = 0;
104
0
  uint16_t hello_option_override_interval = 0;
105
0
  uint32_t hello_option_dr_priority = 0;
106
0
  uint32_t hello_option_generation_id = 0;
107
0
  struct list *hello_option_addr_list = 0;
108
109
0
  if (PIM_DEBUG_PIM_HELLO)
110
0
    on_trace(__func__, ifp, src_addr);
111
112
0
  pim_ifp = ifp->info;
113
0
  assert(pim_ifp);
114
115
0
  if (pim_ifp->pim_passive_enable) {
116
0
    if (PIM_DEBUG_PIM_PACKETS)
117
0
      zlog_debug(
118
0
        "skip receiving PIM message on passive interface %s",
119
0
        ifp->name);
120
0
    return 0;
121
0
  }
122
123
0
  ++pim_ifp->pim_ifstat_hello_recv;
124
125
  /*
126
    Parse PIM hello TLVs
127
   */
128
0
  assert(tlv_buf_size >= 0);
129
0
  tlv_curr = tlv_buf;
130
0
  tlv_pastend = tlv_buf + tlv_buf_size;
131
132
0
  while (tlv_curr < tlv_pastend) {
133
0
    uint16_t option_type;
134
0
    uint16_t option_len;
135
0
    int remain = tlv_pastend - tlv_curr;
136
137
0
    if (remain < PIM_TLV_MIN_SIZE) {
138
0
      if (PIM_DEBUG_PIM_HELLO)
139
0
        zlog_debug(
140
0
          "%s: short PIM hello TLV size=%d < min=%d from %pPAs on interface %s",
141
0
          __func__, remain, PIM_TLV_MIN_SIZE,
142
0
          &src_addr, ifp->name);
143
0
      FREE_ADDR_LIST_THEN_RETURN(-1);
144
0
    }
145
146
0
    option_type = PIM_TLV_GET_TYPE(tlv_curr);
147
0
    tlv_curr += PIM_TLV_TYPE_SIZE;
148
0
    option_len = PIM_TLV_GET_LENGTH(tlv_curr);
149
0
    tlv_curr += PIM_TLV_LENGTH_SIZE;
150
151
0
    if ((tlv_curr + option_len) > tlv_pastend) {
152
0
      if (PIM_DEBUG_PIM_HELLO)
153
0
        zlog_debug(
154
0
          "%s: long PIM hello TLV type=%d length=%d > left=%td from %pPAs on interface %s",
155
0
          __func__, option_type, option_len,
156
0
          tlv_pastend - tlv_curr, &src_addr,
157
0
          ifp->name);
158
0
      FREE_ADDR_LIST_THEN_RETURN(-2);
159
0
    }
160
161
0
    if (PIM_DEBUG_PIM_HELLO)
162
0
      zlog_debug(
163
0
        "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %pPAs on %s",
164
0
        __func__, remain, option_type, option_len,
165
0
        &src_addr, ifp->name);
166
167
0
    switch (option_type) {
168
0
    case PIM_MSG_OPTION_TYPE_HOLDTIME:
169
0
      if (pim_tlv_parse_holdtime(ifp->name, src_addr,
170
0
               &hello_options,
171
0
               &hello_option_holdtime,
172
0
               option_len, tlv_curr)) {
173
0
        FREE_ADDR_LIST_THEN_RETURN(-3);
174
0
      }
175
0
      break;
176
0
    case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
177
0
      if (pim_tlv_parse_lan_prune_delay(
178
0
            ifp->name, src_addr, &hello_options,
179
0
            &hello_option_propagation_delay,
180
0
            &hello_option_override_interval, option_len,
181
0
            tlv_curr)) {
182
0
        FREE_ADDR_LIST_THEN_RETURN(-4);
183
0
      }
184
0
      break;
185
0
    case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
186
0
      if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
187
0
                  &hello_options,
188
0
                  &hello_option_dr_priority,
189
0
                  option_len, tlv_curr)) {
190
0
        FREE_ADDR_LIST_THEN_RETURN(-5);
191
0
      }
192
0
      break;
193
0
    case PIM_MSG_OPTION_TYPE_GENERATION_ID:
194
0
      if (pim_tlv_parse_generation_id(
195
0
            ifp->name, src_addr, &hello_options,
196
0
            &hello_option_generation_id, option_len,
197
0
            tlv_curr)) {
198
0
        FREE_ADDR_LIST_THEN_RETURN(-6);
199
0
      }
200
0
      break;
201
0
    case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
202
0
      if (pim_tlv_parse_addr_list(ifp->name, src_addr,
203
0
                &hello_options,
204
0
                &hello_option_addr_list,
205
0
                option_len, tlv_curr)) {
206
0
        return -7;
207
0
      }
208
0
      break;
209
0
    case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
210
0
      if (PIM_DEBUG_PIM_HELLO)
211
0
        zlog_debug(
212
0
          "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %pPAs on interface %s",
213
0
          __func__, option_type, option_len,
214
0
          &src_addr, ifp->name);
215
0
      break;
216
0
    default:
217
0
      if (PIM_DEBUG_PIM_HELLO)
218
0
        zlog_debug(
219
0
          "%s: ignoring unknown PIM hello TLV type=%d length=%d from %pPAs on interface %s",
220
0
          __func__, option_type, option_len,
221
0
          &src_addr, ifp->name);
222
0
    }
223
224
0
    tlv_curr += option_len;
225
0
  }
226
227
  /*
228
    Check received PIM hello options
229
  */
230
231
0
  if (PIM_DEBUG_PIM_HELLO) {
232
0
    tlv_trace_uint16(__func__, "holdtime", ifp->name, src_addr,
233
0
         PIM_OPTION_IS_SET(hello_options,
234
0
               PIM_OPTION_MASK_HOLDTIME),
235
0
         hello_option_holdtime);
236
0
    tlv_trace_uint16(
237
0
      __func__, "propagation_delay", ifp->name, src_addr,
238
0
      PIM_OPTION_IS_SET(hello_options,
239
0
            PIM_OPTION_MASK_LAN_PRUNE_DELAY),
240
0
      hello_option_propagation_delay);
241
0
    tlv_trace_uint16(
242
0
      __func__, "override_interval", ifp->name, src_addr,
243
0
      PIM_OPTION_IS_SET(hello_options,
244
0
            PIM_OPTION_MASK_LAN_PRUNE_DELAY),
245
0
      hello_option_override_interval);
246
0
    tlv_trace_bool(
247
0
      __func__, "can_disable_join_suppression", ifp->name,
248
0
      src_addr,
249
0
      PIM_OPTION_IS_SET(hello_options,
250
0
            PIM_OPTION_MASK_LAN_PRUNE_DELAY),
251
0
      PIM_OPTION_IS_SET(
252
0
        hello_options,
253
0
        PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
254
0
    tlv_trace_uint32(__func__, "dr_priority", ifp->name, src_addr,
255
0
         PIM_OPTION_IS_SET(hello_options,
256
0
               PIM_OPTION_MASK_DR_PRIORITY),
257
0
         hello_option_dr_priority);
258
0
    tlv_trace_uint32_hex(
259
0
      __func__, "generation_id", ifp->name, src_addr,
260
0
      PIM_OPTION_IS_SET(hello_options,
261
0
            PIM_OPTION_MASK_GENERATION_ID),
262
0
      hello_option_generation_id);
263
0
    tlv_trace_list(__func__, "address_list", ifp->name, src_addr,
264
0
             PIM_OPTION_IS_SET(hello_options,
265
0
             PIM_OPTION_MASK_ADDRESS_LIST),
266
0
             hello_option_addr_list);
267
0
  }
268
269
0
  if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
270
0
    if (PIM_DEBUG_PIM_HELLO)
271
0
      zlog_debug(
272
0
        "%s: PIM hello missing holdtime from %pPAs on interface %s",
273
0
        __func__, &src_addr, ifp->name);
274
0
  }
275
276
  /*
277
    New neighbor?
278
  */
279
280
0
  neigh = pim_neighbor_find(ifp, src_addr, false);
281
0
  if (!neigh) {
282
    /* Add as new neighbor */
283
284
0
    neigh = pim_neighbor_add(
285
0
      ifp, src_addr, hello_options, hello_option_holdtime,
286
0
      hello_option_propagation_delay,
287
0
      hello_option_override_interval,
288
0
      hello_option_dr_priority, hello_option_generation_id,
289
0
      hello_option_addr_list, PIM_NEIGHBOR_SEND_DELAY);
290
0
    if (!neigh) {
291
0
      if (PIM_DEBUG_PIM_HELLO)
292
0
        zlog_warn(
293
0
          "%s: failure creating PIM neighbor %pPAs on interface %s",
294
0
          __func__, &src_addr, ifp->name);
295
0
      FREE_ADDR_LIST_THEN_RETURN(-8);
296
0
    }
297
    /* Forward BSM if required */
298
0
    if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
299
0
      if (PIM_DEBUG_PIM_HELLO)
300
0
        zlog_debug(
301
0
          "%s: forwarding bsm to new nbr failed",
302
0
          __func__);
303
0
    }
304
305
    /* actual addr list has been saved under neighbor */
306
0
    return 0;
307
0
  }
308
309
  /*
310
    Received generation ID ?
311
  */
312
313
0
  if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
314
    /* GenID mismatch ? */
315
0
    if (!PIM_OPTION_IS_SET(neigh->hello_options,
316
0
               PIM_OPTION_MASK_GENERATION_ID)
317
0
        || (hello_option_generation_id != neigh->generation_id)) {
318
      /* GenID mismatch, then replace neighbor */
319
320
0
      if (PIM_DEBUG_PIM_HELLO)
321
0
        zlog_debug(
322
0
          "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %pPAs on %s",
323
0
          __func__, hello_option_generation_id,
324
0
          neigh->generation_id, &src_addr,
325
0
          ifp->name);
326
327
0
      pim_upstream_rpf_genid_changed(pim_ifp->pim,
328
0
                   neigh->source_addr);
329
330
0
      pim_neighbor_delete(ifp, neigh, "GenID mismatch");
331
0
      neigh = pim_neighbor_add(ifp, src_addr, hello_options,
332
0
             hello_option_holdtime,
333
0
             hello_option_propagation_delay,
334
0
             hello_option_override_interval,
335
0
             hello_option_dr_priority,
336
0
             hello_option_generation_id,
337
0
             hello_option_addr_list,
338
0
             PIM_NEIGHBOR_SEND_NOW);
339
0
      if (!neigh) {
340
0
        if (PIM_DEBUG_PIM_HELLO)
341
0
          zlog_debug(
342
0
            "%s: failure re-creating PIM neighbor %pPAs on interface %s",
343
0
            __func__, &src_addr, ifp->name);
344
0
        FREE_ADDR_LIST_THEN_RETURN(-9);
345
0
      }
346
      /* Forward BSM if required */
347
0
      if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
348
0
        if (PIM_DEBUG_PIM_HELLO)
349
0
          zlog_debug(
350
0
            "%s: forwarding bsm to new nbr failed",
351
0
            __func__);
352
0
      }
353
      /* actual addr list is saved under neighbor */
354
0
      return 0;
355
356
0
    } /* GenId mismatch: replace neighbor */
357
358
0
  } /* GenId received */
359
360
  /*
361
    Update existing neighbor
362
  */
363
364
0
  pim_neighbor_update(neigh, hello_options, hello_option_holdtime,
365
0
          hello_option_dr_priority, hello_option_addr_list);
366
  /* actual addr list is saved under neighbor */
367
0
  return 0;
368
0
}
369
370
int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf,
371
      int tlv_buf_size, uint16_t holdtime,
372
      uint32_t dr_priority, uint32_t generation_id,
373
      uint16_t propagation_delay, uint16_t override_interval,
374
      int can_disable_join_suppression)
375
0
{
376
0
  uint8_t *curr = tlv_buf;
377
0
  uint8_t *pastend = tlv_buf + tlv_buf_size;
378
0
  uint8_t *tmp;
379
0
#if PIM_IPV == 4
380
0
  struct pim_interface *pim_ifp = ifp->info;
381
0
  struct pim_instance *pim = pim_ifp->pim;
382
0
#endif
383
384
  /*
385
   * Append options
386
   */
387
388
  /* Holdtime */
389
0
  curr = pim_tlv_append_uint16(curr, pastend,
390
0
             PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
391
0
  if (!curr) {
392
0
    if (PIM_DEBUG_PIM_HELLO) {
393
0
      zlog_debug(
394
0
        "%s: could not set PIM hello Holdtime option for interface %s",
395
0
        __func__, ifp->name);
396
0
    }
397
0
    return -1;
398
0
  }
399
400
  /* LAN Prune Delay */
401
0
  tmp = pim_tlv_append_2uint16(curr, pastend,
402
0
             PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
403
0
             propagation_delay, override_interval);
404
0
  if (!tmp) {
405
0
    if (PIM_DEBUG_PIM_HELLO) {
406
0
      zlog_debug(
407
0
        "%s: could not set PIM LAN Prune Delay option for interface %s",
408
0
        __func__, ifp->name);
409
0
    }
410
0
    return -1;
411
0
  }
412
0
  if (can_disable_join_suppression) {
413
0
    *(curr + 4) |= 0x80; /* enable T bit */
414
0
  }
415
0
  curr = tmp;
416
417
  /* DR Priority */
418
0
  curr = pim_tlv_append_uint32(
419
0
    curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
420
0
  if (!curr) {
421
0
    if (PIM_DEBUG_PIM_HELLO) {
422
0
      zlog_debug(
423
0
        "%s: could not set PIM hello DR Priority option for interface %s",
424
0
        __func__, ifp->name);
425
0
    }
426
0
    return -2;
427
0
  }
428
429
  /* Generation ID */
430
0
  curr = pim_tlv_append_uint32(curr, pastend,
431
0
             PIM_MSG_OPTION_TYPE_GENERATION_ID,
432
0
             generation_id);
433
0
  if (!curr) {
434
0
    if (PIM_DEBUG_PIM_HELLO) {
435
0
      zlog_debug(
436
0
        "%s: could not set PIM hello Generation ID option for interface %s",
437
0
        __func__, ifp->name);
438
0
    }
439
0
    return -3;
440
0
  }
441
442
  /* Secondary Address List */
443
0
  if (ifp->connected->count) {
444
0
    curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp,
445
0
                 PIM_AF);
446
0
    if (!curr) {
447
0
      if (PIM_DEBUG_PIM_HELLO) {
448
0
        zlog_debug(
449
0
          "%s: could not set PIM hello %s Secondary Address List option for interface %s",
450
0
          __func__, PIM_AF_NAME, ifp->name);
451
0
      }
452
0
      return -4;
453
0
    }
454
0
#if PIM_IPV == 4
455
0
    if (pim->send_v6_secondary) {
456
0
      curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp,
457
0
                   AF_INET6);
458
0
      if (!curr) {
459
0
        if (PIM_DEBUG_PIM_HELLO) {
460
0
          zlog_debug(
461
0
            "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
462
0
            __func__, ifp->name);
463
0
        }
464
0
        return -4;
465
0
      }
466
0
    }
467
0
#endif
468
0
  }
469
470
0
  return curr - tlv_buf;
471
0
}
472
473
/*
474
  RFC 4601: 4.3.1.  Sending Hello Messages
475
476
  Thus, if a router needs to send a Join/Prune or Assert message on an
477
  interface on which it has not yet sent a Hello message with the
478
  currently configured IP address, then it MUST immediately send the
479
  relevant Hello message without waiting for the Hello Timer to
480
  expire, followed by the Join/Prune or Assert message.
481
*/
482
void pim_hello_require(struct interface *ifp)
483
0
{
484
0
  struct pim_interface *pim_ifp;
485
486
0
  assert(ifp);
487
488
0
  pim_ifp = ifp->info;
489
490
0
  assert(pim_ifp);
491
492
0
  if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp->flags))
493
0
    return;
494
495
0
  pim_hello_restart_now(ifp); /* Send hello and restart timer */
496
0
}