Coverage Report

Created: 2026-10-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_pim.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 "frrevent.h"
11
#include "memory.h"
12
#include "if.h"
13
#include "network.h"
14
15
#include "pimd.h"
16
#include "pim_instance.h"
17
#include "pim_pim.h"
18
#include "pim_time.h"
19
#include "pim_iface.h"
20
#include "pim_sock.h"
21
#include "pim_str.h"
22
#include "pim_util.h"
23
#include "pim_tlv.h"
24
#include "pim_neighbor.h"
25
#include "pim_hello.h"
26
#include "pim_join.h"
27
#include "pim_assert.h"
28
#include "pim_msg.h"
29
#include "pim_register.h"
30
#include "pim_errors.h"
31
#include "pim_bsm.h"
32
#include <lib/lib_errors.h>
33
34
static void on_pim_hello_send(struct event *t);
35
36
static const char *pim_pim_msgtype2str(enum pim_msg_type type)
37
0
{
38
0
  switch (type) {
39
0
  case PIM_MSG_TYPE_HELLO:
40
0
    return "HELLO";
41
0
  case PIM_MSG_TYPE_REGISTER:
42
0
    return "REGISTER";
43
0
  case PIM_MSG_TYPE_REG_STOP:
44
0
    return "REGSTOP";
45
0
  case PIM_MSG_TYPE_JOIN_PRUNE:
46
0
    return "JOINPRUNE";
47
0
  case PIM_MSG_TYPE_BOOTSTRAP:
48
0
    return "BOOT";
49
0
  case PIM_MSG_TYPE_ASSERT:
50
0
    return "ASSERT";
51
0
  case PIM_MSG_TYPE_GRAFT:
52
0
    return "GRAFT";
53
0
  case PIM_MSG_TYPE_GRAFT_ACK:
54
0
    return "GACK";
55
0
  case PIM_MSG_TYPE_CANDIDATE:
56
0
    return "CANDIDATE";
57
0
  }
58
0
59
0
  return "UNKNOWN";
60
0
}
61
62
static void sock_close(struct interface *ifp)
63
0
{
64
0
  struct pim_interface *pim_ifp = ifp->info;
65
66
0
  if (PIM_DEBUG_PIM_TRACE) {
67
0
    if (pim_ifp->t_pim_sock_read) {
68
0
      zlog_debug(
69
0
        "Cancelling READ event for PIM socket fd=%d on interface %s",
70
0
        pim_ifp->pim_sock_fd, ifp->name);
71
0
    }
72
0
  }
73
0
  EVENT_OFF(pim_ifp->t_pim_sock_read);
74
75
0
  if (PIM_DEBUG_PIM_TRACE) {
76
0
    if (pim_ifp->t_pim_hello_timer) {
77
0
      zlog_debug(
78
0
        "Cancelling PIM hello timer for interface %s",
79
0
        ifp->name);
80
0
    }
81
0
  }
82
0
  EVENT_OFF(pim_ifp->t_pim_hello_timer);
83
84
0
  if (PIM_DEBUG_PIM_TRACE) {
85
0
    zlog_debug("Deleting PIM socket fd=%d on interface %s",
86
0
         pim_ifp->pim_sock_fd, ifp->name);
87
0
  }
88
89
  /*
90
   * If the fd is already deleted no need to do anything here
91
   */
92
0
  if (pim_ifp->pim_sock_fd > 0 && close(pim_ifp->pim_sock_fd)) {
93
0
    zlog_warn(
94
0
      "Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
95
0
      pim_ifp->pim_sock_fd, ifp->name, errno,
96
0
      safe_strerror(errno));
97
0
  }
98
99
0
  pim_ifp->pim_sock_fd = -1;
100
0
  pim_ifp->pim_sock_creation = 0;
101
0
}
102
103
void pim_sock_delete(struct interface *ifp, const char *delete_message)
104
0
{
105
0
  zlog_info("PIM INTERFACE DOWN: on interface %s: %s", ifp->name,
106
0
      delete_message);
107
108
0
  if (!ifp->info) {
109
0
    flog_err(EC_PIM_CONFIG,
110
0
       "%s: %s: but PIM not enabled on interface %s (!)",
111
0
       __func__, delete_message, ifp->name);
112
0
    return;
113
0
  }
114
115
  /*
116
    RFC 4601: 4.3.1.  Sending Hello Messages
117
118
    Before an interface goes down or changes primary IP address, a Hello
119
    message with a zero HoldTime should be sent immediately (with the
120
    old IP address if the IP address changed).
121
  */
122
0
  pim_hello_send(ifp, 0 /* zero-sec holdtime */);
123
124
0
  pim_neighbor_delete_all(ifp, delete_message);
125
126
0
  sock_close(ifp);
127
0
}
128
129
/* For now check dst address for hello, assrt and join/prune is all pim rtr */
130
static bool pim_pkt_dst_addr_ok(enum pim_msg_type type, pim_addr addr)
131
2.41k
{
132
2.41k
  if ((type == PIM_MSG_TYPE_HELLO) || (type == PIM_MSG_TYPE_ASSERT)
133
1.88k
      || (type == PIM_MSG_TYPE_JOIN_PRUNE)) {
134
1.28k
    if (pim_addr_cmp(addr, qpim_all_pim_routers_addr))
135
8
      return false;
136
1.28k
  }
137
138
2.40k
  return true;
139
2.41k
}
140
141
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
142
       pim_sgaddr sg)
143
2.45k
{
144
2.45k
  struct iovec iov[2], *iovp = iov;
145
2.45k
#if PIM_IPV == 4
146
2.45k
  struct ip *ip_hdr = (struct ip *)buf;
147
2.45k
  size_t ip_hlen; /* ip header length in bytes */
148
2.45k
#endif
149
2.45k
  uint8_t *pim_msg;
150
2.45k
  uint32_t pim_msg_len = 0;
151
2.45k
  uint16_t pim_checksum; /* received checksum */
152
2.45k
  uint16_t checksum;     /* computed checksum */
153
2.45k
  struct pim_neighbor *neigh;
154
2.45k
  struct pim_msg_header *header;
155
2.45k
  bool   no_fwd;
156
157
2.45k
#if PIM_IPV == 4
158
2.45k
  if (len <= sizeof(*ip_hdr)) {
159
10
    if (PIM_DEBUG_PIM_PACKETS)
160
0
      zlog_debug(
161
10
        "PIM packet size=%zu shorter than minimum=%zu",
162
10
        len, sizeof(*ip_hdr));
163
10
    return -1;
164
10
  }
165
166
2.44k
  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
167
2.44k
#ifdef FUZZING
168
  /*
169
   * Ensure that the header length is 20 or 24 bytes as is appropriate
170
   * The kernel typically ensures that the passed up ip header meets
171
   * standards, and would actually drop the packet if it didn't meet
172
   * them.  So since we are fuzzing and faking the header bits/bobs
173
   * then let's ensure that we don't get into a weird situation
174
   * where a bad ip_hlen here causes pim_msg_len to wrap around into
175
   * the high billions and then we have an issue later
176
   */
177
2.44k
  if (ip_hlen != 20 && ip_hlen != 24)
178
13
    return -1;
179
2.42k
#endif
180
2.42k
  sg = pim_sgaddr_from_iphdr(ip_hdr);
181
182
2.42k
  pim_msg = buf + ip_hlen;
183
2.42k
  pim_msg_len = len - ip_hlen;
184
#else
185
  struct ipv6_ph phdr = {
186
    .src = sg.src,
187
    .dst = sg.grp,
188
    .ulpl = htonl(len),
189
    .next_hdr = IPPROTO_PIM,
190
  };
191
192
  iovp->iov_base = &phdr;
193
  iovp->iov_len = sizeof(phdr);
194
  iovp++;
195
196
  /* NB: header is not included in IPv6 RX */
197
  pim_msg = buf;
198
  pim_msg_len = len;
199
#endif
200
201
2.42k
  iovp->iov_base = pim_msg;
202
2.42k
  iovp->iov_len = pim_msg_len;
203
2.42k
  iovp++;
204
205
2.42k
  if (pim_msg_len < PIM_PIM_MIN_LEN) {
206
3
    if (PIM_DEBUG_PIM_PACKETS)
207
0
      zlog_debug(
208
3
        "PIM message size=%d shorter than minimum=%d",
209
3
        pim_msg_len, PIM_PIM_MIN_LEN);
210
3
    return -1;
211
3
  }
212
2.42k
  header = (struct pim_msg_header *)pim_msg;
213
214
2.42k
  if (header->ver != PIM_PROTO_VERSION) {
215
6
    if (PIM_DEBUG_PIM_PACKETS)
216
0
      zlog_debug(
217
6
        "Ignoring PIM pkt from %s with unsupported version: %d",
218
6
        ifp->name, header->ver);
219
6
    return -1;
220
6
  }
221
222
  /* save received checksum */
223
2.41k
  pim_checksum = header->checksum;
224
225
  /* for computing checksum */
226
2.41k
  header->checksum = 0;
227
2.41k
  no_fwd = header->Nbit;
228
229
2.41k
  if (header->type == PIM_MSG_TYPE_REGISTER) {
230
148
    if (pim_msg_len < PIM_MSG_REGISTER_LEN) {
231
3
      if (PIM_DEBUG_PIM_PACKETS)
232
0
        zlog_debug("PIM Register Message size=%d shorther than min length %d",
233
3
             pim_msg_len, PIM_MSG_REGISTER_LEN);
234
3
      return -1;
235
3
    }
236
237
#if PIM_IPV == 6
238
    phdr.ulpl = htonl(PIM_MSG_REGISTER_LEN);
239
#endif
240
    /* First 8 byte header checksum */
241
145
    iovp[-1].iov_len = PIM_MSG_REGISTER_LEN;
242
145
    checksum = in_cksumv(iov, iovp - iov);
243
244
145
    if (checksum != pim_checksum) {
245
#if PIM_IPV == 6
246
      phdr.ulpl = htonl(pim_msg_len);
247
#endif
248
144
      iovp[-1].iov_len = pim_msg_len;
249
250
144
      checksum = in_cksumv(iov, iovp - iov);
251
144
      if (checksum != pim_checksum) {
252
144
        if (PIM_DEBUG_PIM_PACKETS)
253
0
          zlog_debug(
254
144
            "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
255
144
            ifp->name, pim_checksum,
256
144
            checksum);
257
#ifndef FUZZING
258
        return -1;
259
#endif
260
144
      }
261
144
    }
262
2.27k
  } else {
263
2.27k
    checksum = in_cksumv(iov, iovp - iov);
264
2.27k
    if (checksum != pim_checksum) {
265
2.27k
      if (PIM_DEBUG_PIM_PACKETS)
266
0
        zlog_debug(
267
2.27k
          "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
268
2.27k
          ifp->name, pim_checksum, checksum);
269
#ifndef FUZZING
270
      return -1;
271
#endif
272
2.27k
    }
273
2.27k
  }
274
275
2.41k
  if (PIM_DEBUG_PIM_PACKETS) {
276
0
    zlog_debug(
277
0
      "Recv PIM %s packet from %pPA to %pPA on %s: pim_version=%d pim_msg_size=%d checksum=%x",
278
0
      pim_pim_msgtype2str(header->type), &sg.src, &sg.grp,
279
0
      ifp->name, header->ver, pim_msg_len, checksum);
280
0
    if (PIM_DEBUG_PIM_PACKETDUMP_RECV)
281
0
      pim_pkt_dump(__func__, pim_msg, pim_msg_len);
282
0
  }
283
284
2.41k
  if (!pim_pkt_dst_addr_ok(header->type, sg.grp)) {
285
8
    zlog_warn(
286
8
      "%s: Ignoring Pkt. Unexpected IP destination %pPA for %s (Expected: all_pim_routers_addr) from %pPA",
287
8
      __func__, &sg.grp, pim_pim_msgtype2str(header->type),
288
8
      &sg.src);
289
8
    return -1;
290
8
  }
291
292
2.40k
  switch (header->type) {
293
377
  case PIM_MSG_TYPE_HELLO:
294
377
    return pim_hello_recv(ifp, sg.src, pim_msg + PIM_MSG_HEADER_LEN,
295
377
              pim_msg_len - PIM_MSG_HEADER_LEN);
296
0
    break;
297
145
  case PIM_MSG_TYPE_REGISTER:
298
145
    return pim_register_recv(ifp, sg.grp, sg.src,
299
145
           pim_msg + PIM_MSG_HEADER_LEN,
300
145
           pim_msg_len - PIM_MSG_HEADER_LEN);
301
0
    break;
302
113
  case PIM_MSG_TYPE_REG_STOP:
303
113
    return pim_register_stop_recv(ifp, pim_msg + PIM_MSG_HEADER_LEN,
304
113
                pim_msg_len - PIM_MSG_HEADER_LEN);
305
0
    break;
306
742
  case PIM_MSG_TYPE_JOIN_PRUNE:
307
742
    neigh = pim_neighbor_find(ifp, sg.src, false);
308
742
    if (!neigh) {
309
2
      if (PIM_DEBUG_PIM_PACKETS)
310
0
        zlog_debug(
311
2
          "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
312
2
          __FILE__, __func__, header->type,
313
2
          &sg.src, ifp->name);
314
2
      return -1;
315
2
    }
316
740
    pim_neighbor_timer_reset(neigh, neigh->holdtime);
317
740
    return pim_joinprune_recv(ifp, neigh, sg.src,
318
740
            pim_msg + PIM_MSG_HEADER_LEN,
319
740
            pim_msg_len - PIM_MSG_HEADER_LEN);
320
0
    break;
321
153
  case PIM_MSG_TYPE_ASSERT:
322
153
    neigh = pim_neighbor_find(ifp, sg.src, false);
323
153
    if (!neigh) {
324
1
      if (PIM_DEBUG_PIM_PACKETS)
325
0
        zlog_debug(
326
1
          "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
327
1
          __FILE__, __func__, header->type,
328
1
          &sg.src, ifp->name);
329
1
      return -1;
330
1
    }
331
152
    pim_neighbor_timer_reset(neigh, neigh->holdtime);
332
152
    return pim_assert_recv(ifp, neigh, sg.src,
333
152
               pim_msg + PIM_MSG_HEADER_LEN,
334
152
               pim_msg_len - PIM_MSG_HEADER_LEN);
335
0
    break;
336
872
  case PIM_MSG_TYPE_BOOTSTRAP:
337
872
    return pim_bsm_process(ifp, &sg, pim_msg, pim_msg_len, no_fwd);
338
0
    break;
339
340
6
  default:
341
6
    if (PIM_DEBUG_PIM_PACKETS) {
342
0
      zlog_debug(
343
0
        "Recv PIM packet type %d which is not currently understood",
344
0
        header->type);
345
0
    }
346
6
    return -1;
347
2.40k
  }
348
2.40k
}
349
350
static void pim_sock_read_on(struct interface *ifp);
351
352
static void pim_sock_read(struct event *t)
353
0
{
354
0
  struct interface *ifp, *orig_ifp;
355
0
  struct pim_interface *pim_ifp;
356
0
  int fd;
357
0
  struct sockaddr_storage from;
358
0
  struct sockaddr_storage to;
359
0
  socklen_t fromlen = sizeof(from);
360
0
  socklen_t tolen = sizeof(to);
361
0
  uint8_t buf[PIM_PIM_BUFSIZE_READ];
362
0
  int len;
363
0
  ifindex_t ifindex = -1;
364
0
  int result = -1; /* defaults to bad */
365
0
  static long long count = 0;
366
0
  int cont = 1;
367
0
368
0
  orig_ifp = ifp = EVENT_ARG(t);
369
0
  fd = EVENT_FD(t);
370
0
371
0
  pim_ifp = ifp->info;
372
0
373
0
  while (cont) {
374
0
    pim_sgaddr sg;
375
0
376
0
    len = pim_socket_recvfromto(fd, buf, sizeof(buf), &from,
377
0
              &fromlen, &to, &tolen, &ifindex);
378
0
    if (len < 0) {
379
0
      if (errno == EINTR)
380
0
        continue;
381
0
      if (errno == EWOULDBLOCK || errno == EAGAIN)
382
0
        break;
383
0
384
0
      if (PIM_DEBUG_PIM_PACKETS)
385
0
        zlog_debug("Received errno: %d %s", errno,
386
0
             safe_strerror(errno));
387
0
      goto done;
388
0
    }
389
0
390
0
    /*
391
0
     * What?  So with vrf's the incoming packet is received
392
0
     * on the vrf interface but recvfromto above returns
393
0
     * the right ifindex, so just use it.  We know
394
0
     * it's the right interface because we bind to it
395
0
     */
396
0
    ifp = if_lookup_by_index(ifindex, pim_ifp->pim->vrf->vrf_id);
397
0
    if (!ifp || !ifp->info) {
398
0
      if (PIM_DEBUG_PIM_PACKETS)
399
0
        zlog_debug(
400
0
          "%s: Received incoming pim packet on interface(%s:%d) not yet configured for pim",
401
0
          __func__, ifp ? ifp->name : "Unknown",
402
0
          ifindex);
403
0
      goto done;
404
0
    }
405
0
#if PIM_IPV == 4
406
0
    sg.src = ((struct sockaddr_in *)&from)->sin_addr;
407
0
    sg.grp = ((struct sockaddr_in *)&to)->sin_addr;
408
0
#else
409
0
    sg.src = ((struct sockaddr_in6 *)&from)->sin6_addr;
410
0
    sg.grp = ((struct sockaddr_in6 *)&to)->sin6_addr;
411
0
#endif
412
0
413
0
    int fail = pim_pim_packet(ifp, buf, len, sg);
414
0
    if (fail) {
415
0
      if (PIM_DEBUG_PIM_PACKETS)
416
0
        zlog_debug("%s: pim_pim_packet() return=%d",
417
0
             __func__, fail);
418
0
      goto done;
419
0
    }
420
0
421
0
    count++;
422
0
    if (count % router->packet_process == 0)
423
0
      cont = 0;
424
0
  }
425
0
426
0
  result = 0; /* good */
427
0
428
0
done:
429
0
  pim_sock_read_on(orig_ifp);
430
0
431
0
  if (result) {
432
0
    ++pim_ifp->pim_ifstat_hello_recvfail;
433
0
  }
434
0
}
435
436
static void pim_sock_read_on(struct interface *ifp)
437
0
{
438
0
  struct pim_interface *pim_ifp;
439
440
0
  assert(ifp);
441
0
  assert(ifp->info);
442
443
0
  pim_ifp = ifp->info;
444
445
0
  if (PIM_DEBUG_PIM_TRACE_DETAIL) {
446
0
    zlog_debug("Scheduling READ event on PIM socket fd=%d",
447
0
         pim_ifp->pim_sock_fd);
448
0
  }
449
0
  event_add_read(router->master, pim_sock_read, ifp, pim_ifp->pim_sock_fd,
450
0
           &pim_ifp->t_pim_sock_read);
451
0
}
452
453
static int pim_sock_open(struct interface *ifp)
454
0
{
455
0
  int fd;
456
0
  struct pim_interface *pim_ifp = ifp->info;
457
458
0
  fd = pim_socket_mcast(IPPROTO_PIM, pim_ifp->primary_address, ifp,
459
0
            0 /* loop=false */);
460
0
  if (fd < 0)
461
0
    return -1;
462
463
0
  if (pim_socket_join(fd, qpim_all_pim_routers_addr,
464
0
          pim_ifp->primary_address, ifp->ifindex, pim_ifp)) {
465
0
    close(fd);
466
0
    return -2;
467
0
  }
468
469
0
  return fd;
470
0
}
471
472
void pim_ifstat_reset(struct interface *ifp)
473
2
{
474
2
  struct pim_interface *pim_ifp;
475
476
2
  assert(ifp);
477
478
2
  pim_ifp = ifp->info;
479
2
  if (!pim_ifp) {
480
0
    return;
481
0
  }
482
483
2
  pim_ifp->pim_ifstat_start = pim_time_monotonic_sec();
484
2
  pim_ifp->pim_ifstat_hello_sent = 0;
485
2
  pim_ifp->pim_ifstat_hello_sendfail = 0;
486
2
  pim_ifp->pim_ifstat_hello_recv = 0;
487
2
  pim_ifp->pim_ifstat_hello_recvfail = 0;
488
2
  pim_ifp->pim_ifstat_bsm_rx = 0;
489
2
  pim_ifp->pim_ifstat_bsm_tx = 0;
490
2
  pim_ifp->pim_ifstat_join_recv = 0;
491
2
  pim_ifp->pim_ifstat_join_send = 0;
492
2
  pim_ifp->pim_ifstat_prune_recv = 0;
493
2
  pim_ifp->pim_ifstat_prune_send = 0;
494
2
  pim_ifp->pim_ifstat_reg_recv = 0;
495
2
  pim_ifp->pim_ifstat_reg_send = 0;
496
2
  pim_ifp->pim_ifstat_reg_stop_recv = 0;
497
2
  pim_ifp->pim_ifstat_reg_stop_send = 0;
498
2
  pim_ifp->pim_ifstat_assert_recv = 0;
499
2
  pim_ifp->pim_ifstat_assert_send = 0;
500
2
  pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
501
2
  pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
502
2
  pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
503
2
  pim_ifp->igmp_ifstat_joins_sent = 0;
504
2
  pim_ifp->igmp_ifstat_joins_failed = 0;
505
2
  pim_ifp->igmp_peak_group_count = 0;
506
2
}
507
508
void pim_sock_reset(struct interface *ifp)
509
2
{
510
2
  struct pim_interface *pim_ifp;
511
512
2
  assert(ifp);
513
2
  assert(ifp->info);
514
515
2
  pim_ifp = ifp->info;
516
517
2
  pim_ifp->primary_address = pim_find_primary_addr(ifp);
518
519
2
  pim_ifp->pim_sock_fd = -1;
520
2
  pim_ifp->pim_sock_creation = 0;
521
2
  pim_ifp->t_pim_sock_read = NULL;
522
523
2
  pim_ifp->t_pim_hello_timer = NULL;
524
2
  pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD;
525
2
  pim_ifp->pim_default_holdtime =
526
2
    -1; /* unset: means 3.5 * pim_hello_period */
527
2
  pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
528
2
  pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
529
2
  pim_ifp->pim_propagation_delay_msec =
530
2
    PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
531
2
  pim_ifp->pim_override_interval_msec =
532
2
    PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
533
2
  pim_ifp->pim_can_disable_join_suppression =
534
2
    PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION;
535
536
  /* neighbors without lan_delay */
537
2
  pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
538
2
  pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
539
2
  pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
540
541
  /* DR Election */
542
2
  pim_ifp->pim_dr_election_last = 0; /* timestamp */
543
2
  pim_ifp->pim_dr_election_count = 0;
544
2
  pim_ifp->pim_dr_election_changes = 0;
545
2
  pim_ifp->pim_dr_num_nondrpri_neighbors =
546
2
    0; /* neighbors without dr_pri */
547
2
  pim_ifp->pim_dr_addr = pim_ifp->primary_address;
548
2
  pim_ifp->am_i_dr = true;
549
550
2
  pim_ifstat_reset(ifp);
551
2
}
552
553
#if PIM_IPV == 4
554
static uint16_t ip_id = 0;
555
#endif
556
557
#if PIM_IPV == 4
558
static int pim_msg_send_frame(int fd, char *buf, size_t len,
559
            struct sockaddr *dst, size_t salen,
560
            const char *ifname)
561
127
{
562
127
  if (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) >= 0)
563
0
    return 0;
564
565
127
  if (errno == EMSGSIZE) {
566
0
    struct ip *ip = (struct ip *)buf;
567
0
    size_t hdrsize = sizeof(struct ip);
568
0
    size_t newlen1 = ((len - hdrsize) / 2) & 0xFFF8;
569
0
    size_t sendlen = newlen1 + hdrsize;
570
0
    size_t offset = ntohs(ip->ip_off);
571
0
    int ret;
572
573
0
    ip->ip_len = htons(sendlen);
574
0
    ip->ip_off = htons(offset | IP_MF);
575
576
0
    ret = pim_msg_send_frame(fd, buf, sendlen, dst, salen, ifname);
577
0
    if (ret)
578
0
      return ret;
579
580
0
    struct ip *ip2 = (struct ip *)(buf + newlen1);
581
0
    size_t newlen2 = len - sendlen;
582
583
0
    sendlen = newlen2 + hdrsize;
584
585
0
    memcpy(ip2, ip, hdrsize);
586
0
    ip2->ip_len = htons(sendlen);
587
0
    ip2->ip_off = htons(offset + (newlen1 >> 3));
588
0
    return pim_msg_send_frame(fd, (char *)ip2, sendlen, dst, salen,
589
0
            ifname);
590
0
  }
591
592
127
  zlog_warn(
593
127
    "%s: sendto() failure to %pSU: iface=%s fd=%d msg_size=%zd: %m",
594
127
    __func__, dst, ifname, fd, len);
595
127
  return -1;
596
127
}
597
598
#else
599
static int pim_msg_send_frame(pim_addr src, pim_addr dst, ifindex_t ifindex,
600
            struct iovec *message, int fd)
601
{
602
  int retval;
603
  struct msghdr smsghdr = {};
604
  struct cmsghdr *scmsgp;
605
  union cmsgbuf {
606
    struct cmsghdr hdr;
607
    uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
608
  };
609
  struct in6_pktinfo *pktinfo;
610
  struct sockaddr_in6 dst_sin6 = {};
611
612
  union cmsgbuf cmsg_buf = {};
613
614
  /* destination address */
615
  dst_sin6.sin6_family = AF_INET6;
616
#ifdef SIN6_LEN
617
  dst_sin6.sin6_len = sizeof(struct sockaddr_in6);
618
#endif /*SIN6_LEN*/
619
  dst_sin6.sin6_addr = dst;
620
  dst_sin6.sin6_scope_id = ifindex;
621
622
  /* send msg hdr */
623
  smsghdr.msg_iov = message;
624
  smsghdr.msg_iovlen = 1;
625
  smsghdr.msg_name = (caddr_t)&dst_sin6;
626
  smsghdr.msg_namelen = sizeof(dst_sin6);
627
  smsghdr.msg_control = (caddr_t)&cmsg_buf.buf;
628
  smsghdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
629
  smsghdr.msg_flags = 0;
630
631
  scmsgp = CMSG_FIRSTHDR(&smsghdr);
632
  scmsgp->cmsg_level = IPPROTO_IPV6;
633
  scmsgp->cmsg_type = IPV6_PKTINFO;
634
  scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
635
636
  pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
637
  pktinfo->ipi6_ifindex = ifindex;
638
  pktinfo->ipi6_addr = src;
639
640
  retval = sendmsg(fd, &smsghdr, 0);
641
  if (retval < 0)
642
    flog_err(
643
      EC_LIB_SOCKET,
644
      "sendmsg failed: source: %pI6 Dest: %pI6 ifindex: %d: %s (%d)",
645
      &src, &dst, ifindex, safe_strerror(errno), errno);
646
647
  return retval;
648
}
649
#endif
650
651
int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
652
     int pim_msg_size, struct interface *ifp)
653
127
{
654
127
  struct pim_interface *pim_ifp;
655
656
657
127
  pim_ifp = ifp->info;
658
659
127
  if (pim_ifp->pim_passive_enable) {
660
0
    if (PIM_DEBUG_PIM_PACKETS)
661
0
      zlog_debug(
662
0
        "skip sending PIM message on passive interface %s",
663
0
        ifp->name);
664
0
    return 0;
665
0
  }
666
667
127
#if PIM_IPV == 4
668
127
  uint8_t ttl;
669
127
  struct pim_msg_header *header;
670
127
  unsigned char buffer[10000];
671
672
127
  memset(buffer, 0, 10000);
673
674
127
  header = (struct pim_msg_header *)pim_msg;
675
676
/*
677
 * Omnios apparently doesn't have a #define for IP default
678
 * ttl that is the same as all other platforms.
679
 */
680
#ifndef IPDEFTTL
681
#define IPDEFTTL   64
682
#endif
683
  /* TTL for packets destine to ALL-PIM-ROUTERS is 1 */
684
127
  switch (header->type) {
685
123
  case PIM_MSG_TYPE_HELLO:
686
123
  case PIM_MSG_TYPE_JOIN_PRUNE:
687
123
  case PIM_MSG_TYPE_BOOTSTRAP:
688
123
  case PIM_MSG_TYPE_ASSERT:
689
123
    ttl = 1;
690
123
    break;
691
0
  case PIM_MSG_TYPE_REGISTER:
692
4
  case PIM_MSG_TYPE_REG_STOP:
693
4
  case PIM_MSG_TYPE_GRAFT:
694
4
  case PIM_MSG_TYPE_GRAFT_ACK:
695
4
  case PIM_MSG_TYPE_CANDIDATE:
696
4
    ttl = IPDEFTTL;
697
4
    break;
698
0
  default:
699
0
    ttl = MAXTTL;
700
0
    break;
701
127
  }
702
703
127
  struct ip *ip = (struct ip *)buffer;
704
127
  struct sockaddr_in to = {};
705
127
  int sendlen = sizeof(*ip) + pim_msg_size;
706
127
  socklen_t tolen;
707
127
  unsigned char *msg_start;
708
709
127
  ip->ip_id = htons(++ip_id);
710
127
  ip->ip_hl = 5;
711
127
  ip->ip_v = 4;
712
127
  ip->ip_tos = IPTOS_PREC_INTERNETCONTROL;
713
127
  ip->ip_p = PIM_IP_PROTO_PIM;
714
127
  ip->ip_src = src;
715
127
  ip->ip_dst = dst;
716
127
  ip->ip_ttl = ttl;
717
127
  ip->ip_len = htons(sendlen);
718
719
127
  to.sin_family = AF_INET;
720
127
  to.sin_addr = dst;
721
127
  tolen = sizeof(to);
722
723
127
  msg_start = buffer + sizeof(*ip);
724
127
  memcpy(msg_start, pim_msg, pim_msg_size);
725
726
127
  if (PIM_DEBUG_PIM_PACKETS)
727
0
    zlog_debug("%s: to %pPA on %s: msg_size=%d checksum=%x",
728
127
         __func__, &dst, ifp->name, pim_msg_size,
729
127
         header->checksum);
730
731
127
  if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
732
0
    pim_pkt_dump(__func__, pim_msg, pim_msg_size);
733
0
  }
734
735
127
  pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
736
127
         tolen, ifp->name);
737
127
  return 0;
738
739
#else
740
  struct iovec iovector[2];
741
742
  iovector[0].iov_base = pim_msg;
743
  iovector[0].iov_len = pim_msg_size;
744
745
  pim_msg_send_frame(src, dst, ifp->ifindex, &iovector[0], fd);
746
747
  return 0;
748
#endif
749
127
}
750
751
static int hello_send(struct interface *ifp, uint16_t holdtime)
752
123
{
753
123
  uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
754
123
  struct pim_interface *pim_ifp;
755
123
  int pim_tlv_size;
756
123
  int pim_msg_size;
757
758
123
  pim_ifp = ifp->info;
759
760
123
  if (PIM_DEBUG_PIM_HELLO)
761
0
    zlog_debug(
762
123
      "%s: to %pPA on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
763
123
      __func__, &qpim_all_pim_routers_addr, ifp->name,
764
123
      holdtime, pim_ifp->pim_propagation_delay_msec,
765
123
      pim_ifp->pim_override_interval_msec,
766
123
      pim_ifp->pim_can_disable_join_suppression,
767
123
      pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
768
123
      listcount(ifp->connected));
769
770
123
  pim_tlv_size = pim_hello_build_tlv(
771
123
    ifp, pim_msg + PIM_PIM_MIN_LEN,
772
123
    sizeof(pim_msg) - PIM_PIM_MIN_LEN, holdtime,
773
123
    pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
774
123
    pim_ifp->pim_propagation_delay_msec,
775
123
    pim_ifp->pim_override_interval_msec,
776
123
    pim_ifp->pim_can_disable_join_suppression);
777
123
  if (pim_tlv_size < 0) {
778
0
    return -1;
779
0
  }
780
781
123
  pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
782
783
123
  assert(pim_msg_size >= PIM_PIM_MIN_LEN);
784
123
  assert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
785
786
123
  pim_msg_build_header(pim_ifp->primary_address,
787
123
           qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
788
123
           PIM_MSG_TYPE_HELLO, false);
789
790
123
  if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
791
123
       qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
792
123
       ifp)) {
793
0
    if (PIM_DEBUG_PIM_HELLO) {
794
0
      zlog_debug(
795
0
        "%s: could not send PIM message on interface %s",
796
0
        __func__, ifp->name);
797
0
    }
798
0
    return -2;
799
0
  }
800
801
123
  return 0;
802
123
}
803
804
int pim_hello_send(struct interface *ifp, uint16_t holdtime)
805
123
{
806
123
  struct pim_interface *pim_ifp = ifp->info;
807
808
123
  if (if_is_loopback(ifp))
809
0
    return 0;
810
811
123
  if (hello_send(ifp, holdtime)) {
812
0
    ++pim_ifp->pim_ifstat_hello_sendfail;
813
814
0
    if (PIM_DEBUG_PIM_HELLO) {
815
0
      zlog_warn("Could not send PIM hello on interface %s",
816
0
          ifp->name);
817
0
    }
818
0
    return -1;
819
0
  }
820
821
123
  if (!pim_ifp->pim_passive_enable) {
822
123
    ++pim_ifp->pim_ifstat_hello_sent;
823
123
    PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
824
123
  }
825
826
123
  return 0;
827
123
}
828
829
static void hello_resched(struct interface *ifp)
830
123
{
831
123
  struct pim_interface *pim_ifp;
832
833
123
  pim_ifp = ifp->info;
834
835
123
  if (PIM_DEBUG_PIM_HELLO) {
836
0
    zlog_debug("Rescheduling %d sec hello on interface %s",
837
0
         pim_ifp->pim_hello_period, ifp->name);
838
0
  }
839
123
  EVENT_OFF(pim_ifp->t_pim_hello_timer);
840
123
  event_add_timer(router->master, on_pim_hello_send, ifp,
841
123
      pim_ifp->pim_hello_period, &pim_ifp->t_pim_hello_timer);
842
123
}
843
844
/*
845
  Periodic hello timer
846
 */
847
static void on_pim_hello_send(struct event *t)
848
0
{
849
0
  struct pim_interface *pim_ifp;
850
0
  struct interface *ifp;
851
0
852
0
  ifp = EVENT_ARG(t);
853
0
  pim_ifp = ifp->info;
854
0
855
0
  /*
856
0
   * Schedule next hello
857
0
   */
858
0
  hello_resched(ifp);
859
0
860
0
  /*
861
0
   * Send hello
862
0
   */
863
0
  pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
864
0
}
865
866
/*
867
  RFC 4601: 4.3.1.  Sending Hello Messages
868
869
  Thus, if a router needs to send a Join/Prune or Assert message on an
870
  interface on which it has not yet sent a Hello message with the
871
  currently configured IP address, then it MUST immediately send the
872
  relevant Hello message without waiting for the Hello Timer to
873
  expire, followed by the Join/Prune or Assert message.
874
 */
875
void pim_hello_restart_now(struct interface *ifp)
876
123
{
877
123
  struct pim_interface *pim_ifp;
878
879
123
  pim_ifp = ifp->info;
880
881
  /*
882
   * Reset next hello timer
883
   */
884
123
  hello_resched(ifp);
885
886
  /*
887
   * Immediately send hello
888
   */
889
123
  pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
890
123
}
891
892
/*
893
  RFC 4601: 4.3.1.  Sending Hello Messages
894
895
  To allow new or rebooting routers to learn of PIM neighbors quickly,
896
  when a Hello message is received from a new neighbor, or a Hello
897
  message with a new GenID is received from an existing neighbor, a
898
  new Hello message should be sent on this interface after a
899
  randomized delay between 0 and Triggered_Hello_Delay.
900
 */
901
void pim_hello_restart_triggered(struct interface *ifp)
902
120
{
903
120
  struct pim_interface *pim_ifp;
904
120
  int triggered_hello_delay_msec;
905
120
  int random_msec;
906
907
120
  pim_ifp = ifp->info;
908
909
  /*
910
   * No need to ever start loopback or vrf device hello's
911
   */
912
120
  if (if_is_loopback(ifp))
913
0
    return;
914
915
  /*
916
   * There exists situations where we have the a RPF out this
917
   * interface, but we haven't formed a neighbor yet.  This
918
   * happens especially during interface flaps.  While
919
   * we would like to handle this more gracefully in other
920
   * parts of the code.  In order to get us up and running
921
   * let's just send the hello immediate'ish
922
   * This should be revisited when we get nexthop tracking
923
   * in and when we have a better handle on safely
924
   * handling the rpf information for upstreams that
925
   * we cannot legally reach yet.
926
   */
927
120
  triggered_hello_delay_msec = 1;
928
  // triggered_hello_delay_msec = 1000 *
929
  // pim_ifp->pim_triggered_hello_delay;
930
931
120
  if (pim_ifp->t_pim_hello_timer) {
932
0
    long remain_msec =
933
0
      pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
934
0
    if (remain_msec <= triggered_hello_delay_msec) {
935
      /* Rescheduling hello would increase the delay, then
936
         it's faster
937
         to just wait for the scheduled periodic hello. */
938
0
      return;
939
0
    }
940
941
0
    EVENT_OFF(pim_ifp->t_pim_hello_timer);
942
0
  }
943
944
120
  random_msec = triggered_hello_delay_msec;
945
  // random_msec = random() % (triggered_hello_delay_msec + 1);
946
947
120
  if (PIM_DEBUG_PIM_HELLO) {
948
0
    zlog_debug("Scheduling %d msec triggered hello on interface %s",
949
0
         random_msec, ifp->name);
950
0
  }
951
952
120
  event_add_timer_msec(router->master, on_pim_hello_send, ifp,
953
120
           random_msec, &pim_ifp->t_pim_hello_timer);
954
120
}
955
956
int pim_sock_add(struct interface *ifp)
957
0
{
958
0
  struct pim_interface *pim_ifp;
959
0
  uint32_t old_genid;
960
961
0
  pim_ifp = ifp->info;
962
0
  assert(pim_ifp);
963
964
0
  if (pim_ifp->pim_sock_fd >= 0) {
965
0
    if (PIM_DEBUG_PIM_PACKETS)
966
0
      zlog_debug(
967
0
        "Can't recreate existing PIM socket fd=%d for interface %s",
968
0
        pim_ifp->pim_sock_fd, ifp->name);
969
0
    return -1;
970
0
  }
971
972
0
  pim_ifp->pim_sock_fd = pim_sock_open(ifp);
973
0
  if (pim_ifp->pim_sock_fd < 0) {
974
0
    if (PIM_DEBUG_PIM_PACKETS)
975
0
      zlog_debug("Could not open PIM socket on interface %s",
976
0
           ifp->name);
977
0
    return -2;
978
0
  }
979
980
0
  pim_socket_ip_hdr(pim_ifp->pim_sock_fd);
981
982
0
  pim_ifp->t_pim_sock_read = NULL;
983
0
  pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
984
985
  /*
986
   * Just ensure that the new generation id
987
   * actually chooses something different.
988
   * Actually ran across a case where this
989
   * happened, pre-switch to random().
990
   * While this is unlikely to happen now
991
   * let's make sure it doesn't.
992
   */
993
0
  old_genid = pim_ifp->pim_generation_id;
994
995
0
  while (old_genid == pim_ifp->pim_generation_id)
996
0
    pim_ifp->pim_generation_id = frr_weak_random();
997
998
0
  zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", ifp->name,
999
0
      ifp->ifindex);
1000
1001
  /*
1002
   * Start receiving PIM messages
1003
   */
1004
0
  pim_sock_read_on(ifp);
1005
1006
  /*
1007
   * Start sending PIM hello's
1008
   */
1009
0
  pim_hello_restart_triggered(ifp);
1010
1011
0
  return 0;
1012
0
}