Coverage Report

Created: 2026-04-27 06:43

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.87k
{
132
2.87k
  if ((type == PIM_MSG_TYPE_HELLO) || (type == PIM_MSG_TYPE_ASSERT)
133
2.22k
      || (type == PIM_MSG_TYPE_JOIN_PRUNE)) {
134
1.48k
    if (pim_addr_cmp(addr, qpim_all_pim_routers_addr))
135
10
      return false;
136
1.48k
  }
137
138
2.86k
  return true;
139
2.87k
}
140
141
int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
142
       pim_sgaddr sg)
143
2.91k
{
144
2.91k
  struct iovec iov[2], *iovp = iov;
145
2.91k
#if PIM_IPV == 4
146
2.91k
  struct ip *ip_hdr = (struct ip *)buf;
147
2.91k
  size_t ip_hlen; /* ip header length in bytes */
148
2.91k
#endif
149
2.91k
  uint8_t *pim_msg;
150
2.91k
  uint32_t pim_msg_len = 0;
151
2.91k
  uint16_t pim_checksum; /* received checksum */
152
2.91k
  uint16_t checksum;     /* computed checksum */
153
2.91k
  struct pim_neighbor *neigh;
154
2.91k
  struct pim_msg_header *header;
155
2.91k
  bool   no_fwd;
156
157
2.91k
#if PIM_IPV == 4
158
2.91k
  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.90k
  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
167
2.90k
#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.90k
  if (ip_hlen != 20 && ip_hlen != 24)
178
11
    return -1;
179
2.89k
#endif
180
2.89k
  sg = pim_sgaddr_from_iphdr(ip_hdr);
181
182
2.89k
  pim_msg = buf + ip_hlen;
183
2.89k
  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.89k
  iovp->iov_base = pim_msg;
202
2.89k
  iovp->iov_len = pim_msg_len;
203
2.89k
  iovp++;
204
205
2.89k
  if (pim_msg_len < PIM_PIM_MIN_LEN) {
206
4
    if (PIM_DEBUG_PIM_PACKETS)
207
0
      zlog_debug(
208
4
        "PIM message size=%d shorter than minimum=%d",
209
4
        pim_msg_len, PIM_PIM_MIN_LEN);
210
4
    return -1;
211
4
  }
212
2.88k
  header = (struct pim_msg_header *)pim_msg;
213
214
2.88k
  if (header->ver != PIM_PROTO_VERSION) {
215
7
    if (PIM_DEBUG_PIM_PACKETS)
216
0
      zlog_debug(
217
7
        "Ignoring PIM pkt from %s with unsupported version: %d",
218
7
        ifp->name, header->ver);
219
7
    return -1;
220
7
  }
221
222
  /* save received checksum */
223
2.88k
  pim_checksum = header->checksum;
224
225
  /* for computing checksum */
226
2.88k
  header->checksum = 0;
227
2.88k
  no_fwd = header->Nbit;
228
229
2.88k
  if (header->type == PIM_MSG_TYPE_REGISTER) {
230
145
    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
142
    iovp[-1].iov_len = PIM_MSG_REGISTER_LEN;
242
142
    checksum = in_cksumv(iov, iovp - iov);
243
244
142
    if (checksum != pim_checksum) {
245
#if PIM_IPV == 6
246
      phdr.ulpl = htonl(pim_msg_len);
247
#endif
248
141
      iovp[-1].iov_len = pim_msg_len;
249
250
141
      checksum = in_cksumv(iov, iovp - iov);
251
141
      if (checksum != pim_checksum) {
252
141
        if (PIM_DEBUG_PIM_PACKETS)
253
0
          zlog_debug(
254
141
            "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
255
141
            ifp->name, pim_checksum,
256
141
            checksum);
257
#ifndef FUZZING
258
        return -1;
259
#endif
260
141
      }
261
141
    }
262
2.73k
  } else {
263
2.73k
    checksum = in_cksumv(iov, iovp - iov);
264
2.73k
    if (checksum != pim_checksum) {
265
2.73k
      if (PIM_DEBUG_PIM_PACKETS)
266
0
        zlog_debug(
267
2.73k
          "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
268
2.73k
          ifp->name, pim_checksum, checksum);
269
#ifndef FUZZING
270
      return -1;
271
#endif
272
2.73k
    }
273
2.73k
  }
274
275
2.87k
  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.87k
  if (!pim_pkt_dst_addr_ok(header->type, sg.grp)) {
285
10
    zlog_warn(
286
10
      "%s: Ignoring Pkt. Unexpected IP destination %pPA for %s (Expected: all_pim_routers_addr) from %pPA",
287
10
      __func__, &sg.grp, pim_pim_msgtype2str(header->type),
288
10
      &sg.src);
289
10
    return -1;
290
10
  }
291
292
2.86k
  switch (header->type) {
293
481
  case PIM_MSG_TYPE_HELLO:
294
481
    return pim_hello_recv(ifp, sg.src, pim_msg + PIM_MSG_HEADER_LEN,
295
481
              pim_msg_len - PIM_MSG_HEADER_LEN);
296
0
    break;
297
142
  case PIM_MSG_TYPE_REGISTER:
298
142
    return pim_register_recv(ifp, sg.grp, sg.src,
299
142
           pim_msg + PIM_MSG_HEADER_LEN,
300
142
           pim_msg_len - PIM_MSG_HEADER_LEN);
301
0
    break;
302
144
  case PIM_MSG_TYPE_REG_STOP:
303
144
    return pim_register_stop_recv(ifp, pim_msg + PIM_MSG_HEADER_LEN,
304
144
                pim_msg_len - PIM_MSG_HEADER_LEN);
305
0
    break;
306
831
  case PIM_MSG_TYPE_JOIN_PRUNE:
307
831
    neigh = pim_neighbor_find(ifp, sg.src, false);
308
831
    if (!neigh) {
309
1
      if (PIM_DEBUG_PIM_PACKETS)
310
0
        zlog_debug(
311
1
          "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
312
1
          __FILE__, __func__, header->type,
313
1
          &sg.src, ifp->name);
314
1
      return -1;
315
1
    }
316
830
    pim_neighbor_timer_reset(neigh, neigh->holdtime);
317
830
    return pim_joinprune_recv(ifp, neigh, sg.src,
318
830
            pim_msg + PIM_MSG_HEADER_LEN,
319
830
            pim_msg_len - PIM_MSG_HEADER_LEN);
320
0
    break;
321
163
  case PIM_MSG_TYPE_ASSERT:
322
163
    neigh = pim_neighbor_find(ifp, sg.src, false);
323
163
    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
162
    pim_neighbor_timer_reset(neigh, neigh->holdtime);
332
162
    return pim_assert_recv(ifp, neigh, sg.src,
333
162
               pim_msg + PIM_MSG_HEADER_LEN,
334
162
               pim_msg_len - PIM_MSG_HEADER_LEN);
335
0
    break;
336
1.10k
  case PIM_MSG_TYPE_BOOTSTRAP:
337
1.10k
    return pim_bsm_process(ifp, &sg, pim_msg, pim_msg_len, no_fwd);
338
0
    break;
339
340
5
  default:
341
5
    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
5
    return -1;
347
2.86k
  }
348
2.86k
}
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
160
{
562
160
  if (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) >= 0)
563
0
    return 0;
564
565
160
  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
160
  zlog_warn(
593
160
    "%s: sendto() failure to %pSU: iface=%s fd=%d msg_size=%zd: %m",
594
160
    __func__, dst, ifname, fd, len);
595
160
  return -1;
596
160
}
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
160
{
654
160
  struct pim_interface *pim_ifp;
655
656
657
160
  pim_ifp = ifp->info;
658
659
160
  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
160
#if PIM_IPV == 4
668
160
  uint8_t ttl;
669
160
  struct pim_msg_header *header;
670
160
  unsigned char buffer[10000];
671
672
160
  memset(buffer, 0, 10000);
673
674
160
  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
160
  switch (header->type) {
685
154
  case PIM_MSG_TYPE_HELLO:
686
154
  case PIM_MSG_TYPE_JOIN_PRUNE:
687
154
  case PIM_MSG_TYPE_BOOTSTRAP:
688
154
  case PIM_MSG_TYPE_ASSERT:
689
154
    ttl = 1;
690
154
    break;
691
0
  case PIM_MSG_TYPE_REGISTER:
692
6
  case PIM_MSG_TYPE_REG_STOP:
693
6
  case PIM_MSG_TYPE_GRAFT:
694
6
  case PIM_MSG_TYPE_GRAFT_ACK:
695
6
  case PIM_MSG_TYPE_CANDIDATE:
696
6
    ttl = IPDEFTTL;
697
6
    break;
698
0
  default:
699
0
    ttl = MAXTTL;
700
0
    break;
701
160
  }
702
703
160
  struct ip *ip = (struct ip *)buffer;
704
160
  struct sockaddr_in to = {};
705
160
  int sendlen = sizeof(*ip) + pim_msg_size;
706
160
  socklen_t tolen;
707
160
  unsigned char *msg_start;
708
709
160
  ip->ip_id = htons(++ip_id);
710
160
  ip->ip_hl = 5;
711
160
  ip->ip_v = 4;
712
160
  ip->ip_tos = IPTOS_PREC_INTERNETCONTROL;
713
160
  ip->ip_p = PIM_IP_PROTO_PIM;
714
160
  ip->ip_src = src;
715
160
  ip->ip_dst = dst;
716
160
  ip->ip_ttl = ttl;
717
160
  ip->ip_len = htons(sendlen);
718
719
160
  to.sin_family = AF_INET;
720
160
  to.sin_addr = dst;
721
160
  tolen = sizeof(to);
722
723
160
  msg_start = buffer + sizeof(*ip);
724
160
  memcpy(msg_start, pim_msg, pim_msg_size);
725
726
160
  if (PIM_DEBUG_PIM_PACKETS)
727
0
    zlog_debug("%s: to %pPA on %s: msg_size=%d checksum=%x",
728
160
         __func__, &dst, ifp->name, pim_msg_size,
729
160
         header->checksum);
730
731
160
  if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
732
0
    pim_pkt_dump(__func__, pim_msg, pim_msg_size);
733
0
  }
734
735
160
  pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
736
160
         tolen, ifp->name);
737
160
  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
160
}
750
751
static int hello_send(struct interface *ifp, uint16_t holdtime)
752
154
{
753
154
  uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
754
154
  struct pim_interface *pim_ifp;
755
154
  int pim_tlv_size;
756
154
  int pim_msg_size;
757
758
154
  pim_ifp = ifp->info;
759
760
154
  if (PIM_DEBUG_PIM_HELLO)
761
0
    zlog_debug(
762
154
      "%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
154
      __func__, &qpim_all_pim_routers_addr, ifp->name,
764
154
      holdtime, pim_ifp->pim_propagation_delay_msec,
765
154
      pim_ifp->pim_override_interval_msec,
766
154
      pim_ifp->pim_can_disable_join_suppression,
767
154
      pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
768
154
      listcount(ifp->connected));
769
770
154
  pim_tlv_size = pim_hello_build_tlv(
771
154
    ifp, pim_msg + PIM_PIM_MIN_LEN,
772
154
    sizeof(pim_msg) - PIM_PIM_MIN_LEN, holdtime,
773
154
    pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
774
154
    pim_ifp->pim_propagation_delay_msec,
775
154
    pim_ifp->pim_override_interval_msec,
776
154
    pim_ifp->pim_can_disable_join_suppression);
777
154
  if (pim_tlv_size < 0) {
778
0
    return -1;
779
0
  }
780
781
154
  pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
782
783
154
  assert(pim_msg_size >= PIM_PIM_MIN_LEN);
784
154
  assert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
785
786
154
  pim_msg_build_header(pim_ifp->primary_address,
787
154
           qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
788
154
           PIM_MSG_TYPE_HELLO, false);
789
790
154
  if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
791
154
       qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
792
154
       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
154
  return 0;
802
154
}
803
804
int pim_hello_send(struct interface *ifp, uint16_t holdtime)
805
154
{
806
154
  struct pim_interface *pim_ifp = ifp->info;
807
808
154
  if (if_is_loopback(ifp))
809
0
    return 0;
810
811
154
  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
154
  if (!pim_ifp->pim_passive_enable) {
822
154
    ++pim_ifp->pim_ifstat_hello_sent;
823
154
    PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
824
154
  }
825
826
154
  return 0;
827
154
}
828
829
static void hello_resched(struct interface *ifp)
830
154
{
831
154
  struct pim_interface *pim_ifp;
832
833
154
  pim_ifp = ifp->info;
834
835
154
  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
154
  EVENT_OFF(pim_ifp->t_pim_hello_timer);
840
154
  event_add_timer(router->master, on_pim_hello_send, ifp,
841
154
      pim_ifp->pim_hello_period, &pim_ifp->t_pim_hello_timer);
842
154
}
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
154
{
877
154
  struct pim_interface *pim_ifp;
878
879
154
  pim_ifp = ifp->info;
880
881
  /*
882
   * Reset next hello timer
883
   */
884
154
  hello_resched(ifp);
885
886
  /*
887
   * Immediately send hello
888
   */
889
154
  pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
890
154
}
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
144
{
903
144
  struct pim_interface *pim_ifp;
904
144
  int triggered_hello_delay_msec;
905
144
  int random_msec;
906
907
144
  pim_ifp = ifp->info;
908
909
  /*
910
   * No need to ever start loopback or vrf device hello's
911
   */
912
144
  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
144
  triggered_hello_delay_msec = 1;
928
  // triggered_hello_delay_msec = 1000 *
929
  // pim_ifp->pim_triggered_hello_delay;
930
931
144
  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
144
  random_msec = triggered_hello_delay_msec;
945
  // random_msec = random() % (triggered_hello_delay_msec + 1);
946
947
144
  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
144
  event_add_timer_msec(router->master, on_pim_hello_send, ifp,
953
144
           random_msec, &pim_ifp->t_pim_hello_timer);
954
144
}
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
}