Coverage Report

Created: 2026-01-25 06:17

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