Coverage Report

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