Coverage Report

Created: 2026-05-25 06:55

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
94
  if (hello_option_addr_list) {                                          \
84
10
    list_delete(&hello_option_addr_list);                          \
85
10
  }
86
87
#define FREE_ADDR_LIST_THEN_RETURN(code)                                       \
88
94
  {                                                                      \
89
94
    FREE_ADDR_LIST                                                 \
90
94
    return (code);                                                 \
91
94
  }
92
93
int pim_hello_recv(struct interface *ifp, pim_addr src_addr, uint8_t *tlv_buf,
94
       int tlv_buf_size)
95
481
{
96
481
  struct pim_interface *pim_ifp;
97
481
  struct pim_neighbor *neigh;
98
481
  uint8_t *tlv_curr;
99
481
  uint8_t *tlv_pastend;
100
481
  pim_hello_options hello_options =
101
481
    0; /* bit array recording options found */
102
481
  uint16_t hello_option_holdtime = 0;
103
481
  uint16_t hello_option_propagation_delay = 0;
104
481
  uint16_t hello_option_override_interval = 0;
105
481
  uint32_t hello_option_dr_priority = 0;
106
481
  uint32_t hello_option_generation_id = 0;
107
481
  struct list *hello_option_addr_list = 0;
108
109
481
  if (PIM_DEBUG_PIM_HELLO)
110
0
    on_trace(__func__, ifp, src_addr);
111
112
481
  pim_ifp = ifp->info;
113
481
  assert(pim_ifp);
114
115
481
  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
481
  ++pim_ifp->pim_ifstat_hello_recv;
124
125
  /*
126
    Parse PIM hello TLVs
127
   */
128
481
  assert(tlv_buf_size >= 0);
129
481
  tlv_curr = tlv_buf;
130
481
  tlv_pastend = tlv_buf + tlv_buf_size;
131
132
18.2k
  while (tlv_curr < tlv_pastend) {
133
17.9k
    uint16_t option_type;
134
17.9k
    uint16_t option_len;
135
17.9k
    int remain = tlv_pastend - tlv_curr;
136
137
17.9k
    if (remain < PIM_TLV_MIN_SIZE) {
138
4
      if (PIM_DEBUG_PIM_HELLO)
139
0
        zlog_debug(
140
4
          "%s: short PIM hello TLV size=%d < min=%d from %pPAs on interface %s",
141
4
          __func__, remain, PIM_TLV_MIN_SIZE,
142
4
          &src_addr, ifp->name);
143
4
      FREE_ADDR_LIST_THEN_RETURN(-1);
144
0
    }
145
146
17.9k
    option_type = PIM_TLV_GET_TYPE(tlv_curr);
147
17.9k
    tlv_curr += PIM_TLV_TYPE_SIZE;
148
17.9k
    option_len = PIM_TLV_GET_LENGTH(tlv_curr);
149
17.9k
    tlv_curr += PIM_TLV_LENGTH_SIZE;
150
151
17.9k
    if ((tlv_curr + option_len) > tlv_pastend) {
152
15
      if (PIM_DEBUG_PIM_HELLO)
153
0
        zlog_debug(
154
15
          "%s: long PIM hello TLV type=%d length=%d > left=%td from %pPAs on interface %s",
155
15
          __func__, option_type, option_len,
156
15
          tlv_pastend - tlv_curr, &src_addr,
157
15
          ifp->name);
158
15
      FREE_ADDR_LIST_THEN_RETURN(-2);
159
0
    }
160
161
17.9k
    if (PIM_DEBUG_PIM_HELLO)
162
0
      zlog_debug(
163
17.9k
        "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %pPAs on %s",
164
17.9k
        __func__, remain, option_type, option_len,
165
17.9k
        &src_addr, ifp->name);
166
167
17.9k
    switch (option_type) {
168
230
    case PIM_MSG_OPTION_TYPE_HOLDTIME:
169
230
      if (pim_tlv_parse_holdtime(ifp->name, src_addr,
170
230
               &hello_options,
171
230
               &hello_option_holdtime,
172
230
               option_len, tlv_curr)) {
173
8
        FREE_ADDR_LIST_THEN_RETURN(-3);
174
0
      }
175
222
      break;
176
524
    case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
177
524
      if (pim_tlv_parse_lan_prune_delay(
178
524
            ifp->name, src_addr, &hello_options,
179
524
            &hello_option_propagation_delay,
180
524
            &hello_option_override_interval, option_len,
181
524
            tlv_curr)) {
182
26
        FREE_ADDR_LIST_THEN_RETURN(-4);
183
0
      }
184
498
      break;
185
498
    case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
186
335
      if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
187
335
                  &hello_options,
188
335
                  &hello_option_dr_priority,
189
335
                  option_len, tlv_curr)) {
190
24
        FREE_ADDR_LIST_THEN_RETURN(-5);
191
0
      }
192
311
      break;
193
471
    case PIM_MSG_OPTION_TYPE_GENERATION_ID:
194
471
      if (pim_tlv_parse_generation_id(
195
471
            ifp->name, src_addr, &hello_options,
196
471
            &hello_option_generation_id, option_len,
197
471
            tlv_curr)) {
198
17
        FREE_ADDR_LIST_THEN_RETURN(-6);
199
0
      }
200
454
      break;
201
15.4k
    case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
202
15.4k
      if (pim_tlv_parse_addr_list(ifp->name, src_addr,
203
15.4k
                &hello_options,
204
15.4k
                &hello_option_addr_list,
205
15.4k
                option_len, tlv_curr)) {
206
59
        return -7;
207
59
      }
208
15.3k
      break;
209
15.3k
    case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
210
1
      if (PIM_DEBUG_PIM_HELLO)
211
0
        zlog_debug(
212
1
          "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %pPAs on interface %s",
213
1
          __func__, option_type, option_len,
214
1
          &src_addr, ifp->name);
215
1
      break;
216
949
    default:
217
949
      if (PIM_DEBUG_PIM_HELLO)
218
0
        zlog_debug(
219
17.9k
          "%s: ignoring unknown PIM hello TLV type=%d length=%d from %pPAs on interface %s",
220
17.9k
          __func__, option_type, option_len,
221
17.9k
          &src_addr, ifp->name);
222
17.9k
    }
223
224
17.7k
    tlv_curr += option_len;
225
17.7k
  }
226
227
  /*
228
    Check received PIM hello options
229
  */
230
231
328
  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
328
  if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
270
319
    if (PIM_DEBUG_PIM_HELLO)
271
0
      zlog_debug(
272
319
        "%s: PIM hello missing holdtime from %pPAs on interface %s",
273
319
        __func__, &src_addr, ifp->name);
274
319
  }
275
276
  /*
277
    New neighbor?
278
  */
279
280
328
  neigh = pim_neighbor_find(ifp, src_addr, false);
281
328
  if (!neigh) {
282
    /* Add as new neighbor */
283
284
143
    neigh = pim_neighbor_add(
285
143
      ifp, src_addr, hello_options, hello_option_holdtime,
286
143
      hello_option_propagation_delay,
287
143
      hello_option_override_interval,
288
143
      hello_option_dr_priority, hello_option_generation_id,
289
143
      hello_option_addr_list, PIM_NEIGHBOR_SEND_DELAY);
290
143
    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
143
    if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
299
143
      if (PIM_DEBUG_PIM_HELLO)
300
0
        zlog_debug(
301
143
          "%s: forwarding bsm to new nbr failed",
302
143
          __func__);
303
143
    }
304
305
    /* actual addr list has been saved under neighbor */
306
143
    return 0;
307
143
  }
308
309
  /*
310
    Received generation ID ?
311
  */
312
313
185
  if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
314
    /* GenID mismatch ? */
315
71
    if (!PIM_OPTION_IS_SET(neigh->hello_options,
316
71
               PIM_OPTION_MASK_GENERATION_ID)
317
68
        || (hello_option_generation_id != neigh->generation_id)) {
318
      /* GenID mismatch, then replace neighbor */
319
320
68
      if (PIM_DEBUG_PIM_HELLO)
321
0
        zlog_debug(
322
68
          "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %pPAs on %s",
323
68
          __func__, hello_option_generation_id,
324
68
          neigh->generation_id, &src_addr,
325
68
          ifp->name);
326
327
68
      pim_upstream_rpf_genid_changed(pim_ifp->pim,
328
68
                   neigh->source_addr);
329
330
68
      pim_neighbor_delete(ifp, neigh, "GenID mismatch");
331
68
      neigh = pim_neighbor_add(ifp, src_addr, hello_options,
332
68
             hello_option_holdtime,
333
68
             hello_option_propagation_delay,
334
68
             hello_option_override_interval,
335
68
             hello_option_dr_priority,
336
68
             hello_option_generation_id,
337
68
             hello_option_addr_list,
338
68
             PIM_NEIGHBOR_SEND_NOW);
339
68
      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
68
      if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
348
68
        if (PIM_DEBUG_PIM_HELLO)
349
0
          zlog_debug(
350
68
            "%s: forwarding bsm to new nbr failed",
351
68
            __func__);
352
68
      }
353
      /* actual addr list is saved under neighbor */
354
68
      return 0;
355
356
68
    } /* GenId mismatch: replace neighbor */
357
358
71
  } /* GenId received */
359
360
  /*
361
    Update existing neighbor
362
  */
363
364
117
  pim_neighbor_update(neigh, hello_options, hello_option_holdtime,
365
117
          hello_option_dr_priority, hello_option_addr_list);
366
  /* actual addr list is saved under neighbor */
367
117
  return 0;
368
185
}
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
154
{
376
154
  uint8_t *curr = tlv_buf;
377
154
  uint8_t *pastend = tlv_buf + tlv_buf_size;
378
154
  uint8_t *tmp;
379
154
#if PIM_IPV == 4
380
154
  struct pim_interface *pim_ifp = ifp->info;
381
154
  struct pim_instance *pim = pim_ifp->pim;
382
154
#endif
383
384
  /*
385
   * Append options
386
   */
387
388
  /* Holdtime */
389
154
  curr = pim_tlv_append_uint16(curr, pastend,
390
154
             PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
391
154
  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
154
  tmp = pim_tlv_append_2uint16(curr, pastend,
402
154
             PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
403
154
             propagation_delay, override_interval);
404
154
  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
154
  if (can_disable_join_suppression) {
413
0
    *(curr + 4) |= 0x80; /* enable T bit */
414
0
  }
415
154
  curr = tmp;
416
417
  /* DR Priority */
418
154
  curr = pim_tlv_append_uint32(
419
154
    curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
420
154
  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
154
  curr = pim_tlv_append_uint32(curr, pastend,
431
154
             PIM_MSG_OPTION_TYPE_GENERATION_ID,
432
154
             generation_id);
433
154
  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
154
  if (ifp->connected->count) {
444
154
    curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp,
445
154
                 PIM_AF);
446
154
    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
154
#if PIM_IPV == 4
455
154
    if (pim->send_v6_secondary) {
456
154
      curr = pim_tlv_append_addrlist_ucast(curr, pastend, ifp,
457
154
                   AF_INET6);
458
154
      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
154
    }
467
154
#endif
468
154
  }
469
470
154
  return curr - tlv_buf;
471
154
}
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
724
{
484
724
  struct pim_interface *pim_ifp;
485
486
724
  assert(ifp);
487
488
724
  pim_ifp = ifp->info;
489
490
724
  assert(pim_ifp);
491
492
724
  if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp->flags))
493
638
    return;
494
495
86
  pim_hello_restart_now(ifp); /* Send hello and restart timer */
496
86
}