Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_zlookup.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 "prefix.h"
11
#include "zclient.h"
12
#include "stream.h"
13
#include "network.h"
14
#include "frrevent.h"
15
#include "prefix.h"
16
#include "vty.h"
17
#include "lib_errors.h"
18
19
#include "pimd.h"
20
#include "pim_instance.h"
21
#include "pim_iface.h"
22
#include "pim_neighbor.h"
23
#include "pim_pim.h"
24
#include "pim_str.h"
25
#include "pim_oil.h"
26
#include "pim_zlookup.h"
27
#include "pim_addr.h"
28
29
static struct zclient *zlookup = NULL;
30
struct event *zlookup_read;
31
32
static void zclient_lookup_sched(struct zclient *zlookup, int delay);
33
static void zclient_lookup_read_pipe(struct event *thread);
34
35
/* Connect to zebra for nexthop lookup. */
36
static void zclient_lookup_connect(struct event *t)
37
0
{
38
0
  struct zclient *zlookup;
39
0
40
0
  zlookup = EVENT_ARG(t);
41
0
42
0
  if (zlookup->sock >= 0) {
43
0
    return;
44
0
  }
45
0
46
0
  if (zclient_socket_connect(zlookup) < 0) {
47
0
    ++zlookup->fail;
48
0
    zlog_warn("%s: failure connecting zclient socket: failures=%d",
49
0
        __func__, zlookup->fail);
50
0
  } else {
51
0
    zlookup->fail = 0; /* reset counter on connection */
52
0
  }
53
0
54
0
  if (zclient_send_hello(zlookup) == ZCLIENT_SEND_FAILURE) {
55
0
    if (close(zlookup->sock)) {
56
0
      zlog_warn("%s: closing fd=%d: errno=%d %s", __func__,
57
0
          zlookup->sock, errno, safe_strerror(errno));
58
0
    }
59
0
    zlookup->sock = -1;
60
0
  }
61
0
62
0
  if (zlookup->sock < 0) {
63
0
    /* Since last connect failed, retry within 10 secs */
64
0
    zclient_lookup_sched(zlookup, 10);
65
0
    return;
66
0
  }
67
0
68
0
  event_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60,
69
0
      &zlookup_read);
70
0
}
71
72
/* Schedule connection with delay. */
73
static void zclient_lookup_sched(struct zclient *zlookup, int delay)
74
0
{
75
0
  event_add_timer(router->master, zclient_lookup_connect, zlookup, delay,
76
0
      &zlookup->t_connect);
77
0
78
0
  zlog_notice("%s: zclient lookup connection scheduled for %d seconds",
79
0
        __func__, delay);
80
0
}
81
82
/* Schedule connection for now. */
83
static void zclient_lookup_sched_now(struct zclient *zlookup)
84
83.3k
{
85
83.3k
  event_add_event(router->master, zclient_lookup_connect, zlookup, 0,
86
83.3k
      &zlookup->t_connect);
87
88
83.3k
  zlog_notice("%s: zclient lookup immediate connection scheduled",
89
83.3k
        __func__);
90
83.3k
}
91
92
/* Schedule reconnection, if needed. */
93
static void zclient_lookup_reconnect(struct zclient *zlookup)
94
83.3k
{
95
83.3k
  if (zlookup->t_connect) {
96
0
    return;
97
0
  }
98
99
83.3k
  zclient_lookup_sched_now(zlookup);
100
83.3k
}
101
102
static void zclient_lookup_failed(struct zclient *zlookup)
103
83.3k
{
104
83.3k
  if (zlookup->sock >= 0) {
105
0
    if (close(zlookup->sock)) {
106
0
      zlog_warn("%s: closing fd=%d: errno=%d %s", __func__,
107
0
          zlookup->sock, errno, safe_strerror(errno));
108
0
    }
109
0
    zlookup->sock = -1;
110
0
  }
111
112
83.3k
  zclient_lookup_reconnect(zlookup);
113
83.3k
}
114
115
void zclient_lookup_free(void)
116
0
{
117
0
  EVENT_OFF(zlookup_read);
118
0
  zclient_stop(zlookup);
119
0
  zclient_free(zlookup);
120
0
  zlookup = NULL;
121
0
}
122
123
void zclient_lookup_new(void)
124
1
{
125
1
  struct zclient_options options = zclient_options_default;
126
1
  options.synchronous = true;
127
128
1
  zlookup = zclient_new(router->master, &options, NULL, 0);
129
1
  if (!zlookup) {
130
0
    flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_new() failure",
131
0
       __func__);
132
0
    return;
133
0
  }
134
135
1
  zlookup->sock = -1;
136
1
  zlookup->t_connect = NULL;
137
1
  zlookup->privs = &pimd_privs;
138
139
1
  zclient_lookup_sched_now(zlookup);
140
141
1
  zlog_notice("%s: zclient lookup socket initialized", __func__);
142
1
}
143
144
static int zclient_read_nexthop(struct pim_instance *pim,
145
        struct zclient *zlookup,
146
        struct pim_zlookup_nexthop nexthop_tab[],
147
        const int tab_size, pim_addr addr)
148
0
{
149
0
  int num_ifindex = 0;
150
0
  struct stream *s;
151
0
  uint16_t length;
152
0
  uint8_t marker;
153
0
  uint8_t version;
154
0
  vrf_id_t vrf_id;
155
0
  uint16_t command = 0;
156
0
  struct ipaddr raddr;
157
0
  uint8_t distance;
158
0
  uint32_t metric;
159
0
  int nexthop_num;
160
0
  int i, err;
161
162
0
  if (PIM_DEBUG_PIM_NHT_DETAIL)
163
0
    zlog_debug("%s: addr=%pPAs(%s)", __func__, &addr,
164
0
         pim->vrf->name);
165
166
0
  s = zlookup->ibuf;
167
168
0
  while (command != ZEBRA_NEXTHOP_LOOKUP_MRIB) {
169
0
    stream_reset(s);
170
0
    err = zclient_read_header(s, zlookup->sock, &length, &marker,
171
0
            &version, &vrf_id, &command);
172
0
    if (err < 0) {
173
0
      flog_err(EC_LIB_ZAPI_MISSMATCH,
174
0
         "%s: zclient_read_header() failed", __func__);
175
0
      zclient_lookup_failed(zlookup);
176
0
      return -1;
177
0
    }
178
179
0
    if (command == ZEBRA_ERROR) {
180
0
      enum zebra_error_types error;
181
182
0
      zapi_error_decode(s, &error);
183
      /* Do nothing with it for now */
184
0
      return -1;
185
0
    }
186
0
  }
187
188
0
  stream_get_ipaddr(s, &raddr);
189
190
0
  if (raddr.ipa_type != PIM_IPADDR ||
191
0
      pim_addr_cmp(raddr.ipaddr_pim, addr)) {
192
0
    zlog_warn("%s: address mismatch: addr=%pPA(%s) raddr=%pIA",
193
0
        __func__, &addr, pim->vrf->name, &raddr);
194
    /* warning only */
195
0
  }
196
197
0
  distance = stream_getc(s);
198
0
  metric = stream_getl(s);
199
0
  nexthop_num = stream_getc(s);
200
201
0
  if (nexthop_num < 1 || nexthop_num > router->multipath) {
202
0
    if (PIM_DEBUG_PIM_NHT_DETAIL)
203
0
      zlog_debug("%s: socket %d bad nexthop_num=%d", __func__,
204
0
           zlookup->sock, nexthop_num);
205
0
    return -6;
206
0
  }
207
208
0
  for (i = 0; i < nexthop_num; ++i) {
209
0
    vrf_id_t nexthop_vrf_id;
210
0
    enum nexthop_types_t nexthop_type;
211
0
    struct in_addr nh_ip4;
212
0
    struct in6_addr nh_ip6;
213
0
    ifindex_t nh_ifi;
214
215
0
    nexthop_vrf_id = stream_getl(s);
216
0
    nexthop_type = stream_getc(s);
217
0
    if (num_ifindex >= tab_size) {
218
0
      zlog_warn(
219
0
        "%s: found too many nexthop ifindexes (%d > %d) for address %pPAs(%s)",
220
0
        __func__, (num_ifindex + 1), tab_size, &addr,
221
0
        pim->vrf->name);
222
0
      return num_ifindex;
223
0
    }
224
0
    nexthop_tab[num_ifindex].protocol_distance = distance;
225
0
    nexthop_tab[num_ifindex].route_metric = metric;
226
0
    nexthop_tab[num_ifindex].vrf_id = nexthop_vrf_id;
227
0
    switch (nexthop_type) {
228
0
    case NEXTHOP_TYPE_IFINDEX:
229
0
      nexthop_tab[num_ifindex].ifindex = stream_getl(s);
230
      /*
231
       * Connected route (i.e. no nexthop), use
232
       * address passed in as PIM nexthop.  This will
233
       * allow us to work in cases where we are
234
       * trying to find a route for this box.
235
       */
236
0
      nexthop_tab[num_ifindex].nexthop_addr = addr;
237
0
      ++num_ifindex;
238
0
      break;
239
0
    case NEXTHOP_TYPE_IPV4_IFINDEX:
240
0
    case NEXTHOP_TYPE_IPV4:
241
0
      nh_ip4.s_addr = stream_get_ipv4(s);
242
0
      nh_ifi = stream_getl(s);
243
0
#if PIM_IPV == 4
244
0
      nexthop_tab[num_ifindex].nexthop_addr = nh_ip4;
245
0
      nexthop_tab[num_ifindex].ifindex = nh_ifi;
246
0
      ++num_ifindex;
247
#else
248
      zlog_warn(
249
        "cannot use IPv4 nexthop %pI4(%d) for IPv6 %pPA",
250
        &nh_ip4, nh_ifi, &addr);
251
#endif
252
0
      break;
253
0
    case NEXTHOP_TYPE_IPV6:
254
0
    case NEXTHOP_TYPE_IPV6_IFINDEX:
255
0
      stream_get(&nh_ip6, s, sizeof(nh_ip6));
256
0
      nh_ifi = stream_getl(s);
257
258
#if PIM_IPV == 6
259
      nexthop_tab[num_ifindex].nexthop_addr = nh_ip6;
260
      nexthop_tab[num_ifindex].ifindex = nh_ifi;
261
      ++num_ifindex;
262
#else
263
      /* RFC 5549 v4-over-v6 nexthop handling */
264
265
      /*
266
       * If we are sending v6 secondary assume we receive v6
267
       * secondary
268
       */
269
0
      struct interface *ifp = if_lookup_by_index(
270
0
        nh_ifi,
271
0
        nexthop_vrf_id);
272
273
0
      if (!ifp)
274
0
        break;
275
276
0
      struct pim_neighbor *nbr;
277
278
0
      if (pim->send_v6_secondary) {
279
0
        struct prefix p;
280
281
0
        p.family = AF_INET6;
282
0
        p.prefixlen = IPV6_MAX_BITLEN;
283
0
        p.u.prefix6 = nh_ip6;
284
285
0
        nbr = pim_neighbor_find_by_secondary(ifp, &p);
286
0
      } else
287
0
        nbr = pim_neighbor_find_if(ifp);
288
289
0
      if (!nbr)
290
0
        break;
291
292
0
      nexthop_tab[num_ifindex].nexthop_addr =
293
0
        nbr->source_addr;
294
0
      nexthop_tab[num_ifindex].ifindex = nh_ifi;
295
0
      ++num_ifindex;
296
0
#endif
297
0
      break;
298
0
    case NEXTHOP_TYPE_BLACKHOLE:
299
      /* do nothing */
300
0
      zlog_warn(
301
0
        "%s: found non-ifindex nexthop type=%d for address %pPAs(%s)",
302
0
        __func__, nexthop_type, &addr, pim->vrf->name);
303
0
      break;
304
0
    }
305
0
  }
306
307
0
  return num_ifindex;
308
0
}
309
310
static int zclient_lookup_nexthop_once(struct pim_instance *pim,
311
               struct pim_zlookup_nexthop nexthop_tab[],
312
               const int tab_size, pim_addr addr)
313
83.3k
{
314
83.3k
  struct stream *s;
315
83.3k
  int ret;
316
83.3k
  struct ipaddr ipaddr;
317
318
83.3k
  if (PIM_DEBUG_PIM_NHT_DETAIL)
319
0
    zlog_debug("%s: addr=%pPAs(%s)", __func__, &addr,
320
83.3k
         pim->vrf->name);
321
322
  /* Check socket. */
323
83.3k
  if (zlookup->sock < 0) {
324
83.3k
    flog_err(EC_LIB_ZAPI_SOCKET,
325
83.3k
       "%s: zclient lookup socket is not connected",
326
83.3k
       __func__);
327
83.3k
    zclient_lookup_failed(zlookup);
328
83.3k
    return -1;
329
83.3k
  }
330
331
0
  if (pim->vrf->vrf_id == VRF_UNKNOWN) {
332
0
    zlog_notice(
333
0
      "%s: VRF: %s does not fully exist yet, delaying lookup",
334
0
      __func__, pim->vrf->name);
335
0
    return -1;
336
0
  }
337
338
0
  ipaddr.ipa_type = PIM_IPADDR;
339
0
  ipaddr.ipaddr_pim = addr;
340
341
0
  s = zlookup->obuf;
342
0
  stream_reset(s);
343
0
  zclient_create_header(s, ZEBRA_NEXTHOP_LOOKUP_MRIB, pim->vrf->vrf_id);
344
0
  stream_put_ipaddr(s, &ipaddr);
345
0
  stream_putw_at(s, 0, stream_get_endp(s));
346
347
0
  ret = writen(zlookup->sock, s->data, stream_get_endp(s));
348
0
  if (ret < 0) {
349
0
    flog_err(
350
0
      EC_LIB_SOCKET,
351
0
      "%s: writen() failure: %d writing to zclient lookup socket",
352
0
      __func__, errno);
353
0
    zclient_lookup_failed(zlookup);
354
0
    return -2;
355
0
  }
356
0
  if (ret == 0) {
357
0
    flog_err_sys(EC_LIB_SOCKET,
358
0
           "%s: connection closed on zclient lookup socket",
359
0
           __func__);
360
0
    zclient_lookup_failed(zlookup);
361
0
    return -3;
362
0
  }
363
364
0
  return zclient_read_nexthop(pim, zlookup, nexthop_tab, tab_size, addr);
365
0
}
366
367
void zclient_lookup_read_pipe(struct event *thread)
368
0
{
369
0
  struct zclient *zlookup = EVENT_ARG(thread);
370
0
  struct pim_instance *pim = pim_get_pim_instance(VRF_DEFAULT);
371
0
  struct pim_zlookup_nexthop nexthop_tab[10];
372
0
  pim_addr l = PIMADDR_ANY;
373
0
374
0
  if (!pim) {
375
0
    if (PIM_DEBUG_PIM_NHT_DETAIL)
376
0
      zlog_debug("%s: Unable to find pim instance", __func__);
377
0
    return;
378
0
  }
379
0
380
0
  zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l);
381
0
  event_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60,
382
0
      &zlookup_read);
383
0
}
384
385
int zclient_lookup_nexthop(struct pim_instance *pim,
386
         struct pim_zlookup_nexthop nexthop_tab[],
387
         const int tab_size, pim_addr addr,
388
         int max_lookup)
389
83.3k
{
390
83.3k
  int lookup;
391
83.3k
  uint32_t route_metric = 0xFFFFFFFF;
392
83.3k
  uint8_t protocol_distance = 0xFF;
393
394
83.3k
  pim->nexthop_lookups++;
395
396
83.3k
  for (lookup = 0; lookup < max_lookup; ++lookup) {
397
83.3k
    int num_ifindex;
398
83.3k
    int first_ifindex;
399
83.3k
    pim_addr nexthop_addr;
400
401
83.3k
    num_ifindex = zclient_lookup_nexthop_once(pim, nexthop_tab,
402
83.3k
                tab_size, addr);
403
83.3k
    if (num_ifindex < 1) {
404
83.3k
      if (PIM_DEBUG_PIM_NHT_DETAIL)
405
0
        zlog_debug(
406
83.3k
          "%s: lookup=%d/%d: could not find nexthop ifindex for address %pPA(%s)",
407
83.3k
          __func__, lookup, max_lookup, &addr,
408
83.3k
          pim->vrf->name);
409
83.3k
      return -1;
410
83.3k
    }
411
412
0
    if (lookup < 1) {
413
      /* this is the non-recursive lookup - save original
414
       * metric/distance */
415
0
      route_metric = nexthop_tab[0].route_metric;
416
0
      protocol_distance = nexthop_tab[0].protocol_distance;
417
0
    }
418
419
    /*
420
     * FIXME: Non-recursive nexthop ensured only for first ifindex.
421
     * However, recursive route lookup should really be fixed in
422
     * zebra daemon.
423
     * See also TODO T24.
424
     *
425
     * So Zebra for NEXTHOP_TYPE_IPV4 returns the ifindex now since
426
     * it was being stored.  This Doesn't solve all cases of
427
     * recursive lookup but for the most common types it does.
428
     */
429
0
    first_ifindex = nexthop_tab[0].ifindex;
430
0
    nexthop_addr = nexthop_tab[0].nexthop_addr;
431
0
    if (first_ifindex > 0) {
432
      /* found: first ifindex is non-recursive nexthop */
433
434
0
      if (lookup > 0) {
435
        /* Report non-recursive success after first
436
         * lookup */
437
0
        if (PIM_DEBUG_PIM_NHT)
438
0
          zlog_debug(
439
0
            "%s: lookup=%d/%d: found non-recursive ifindex=%d for address %pPA(%s) dist=%d met=%d",
440
0
            __func__, lookup, max_lookup,
441
0
            first_ifindex, &addr,
442
0
            pim->vrf->name,
443
0
            nexthop_tab[0]
444
0
              .protocol_distance,
445
0
            nexthop_tab[0].route_metric);
446
447
        /* use last address as nexthop address */
448
0
        nexthop_tab[0].nexthop_addr = addr;
449
450
        /* report original route metric/distance */
451
0
        nexthop_tab[0].route_metric = route_metric;
452
0
        nexthop_tab[0].protocol_distance =
453
0
          protocol_distance;
454
0
      }
455
456
0
      return num_ifindex;
457
0
    }
458
459
0
    if (PIM_DEBUG_PIM_NHT)
460
0
      zlog_debug(
461
0
        "%s: lookup=%d/%d: zebra returned recursive nexthop %pPAs for address %pPA(%s) dist=%d met=%d",
462
0
        __func__, lookup, max_lookup, &nexthop_addr,
463
0
        &addr, pim->vrf->name,
464
0
        nexthop_tab[0].protocol_distance,
465
0
        nexthop_tab[0].route_metric);
466
467
0
    addr = nexthop_addr; /* use nexthop
468
          addr for recursive lookup */
469
470
0
  } /* for (max_lookup) */
471
472
0
  if (PIM_DEBUG_PIM_NHT)
473
0
    zlog_warn(
474
0
      "%s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %pPA(%s)",
475
0
      __func__, lookup, max_lookup, &addr, pim->vrf->name);
476
477
0
  return -2;
478
83.3k
}
479
480
void pim_zlookup_show_ip_multicast(struct vty *vty)
481
0
{
482
0
  vty_out(vty, "Zclient lookup socket: ");
483
0
  if (zlookup) {
484
0
    vty_out(vty, "%d failures=%d\n", zlookup->sock, zlookup->fail);
485
0
  } else {
486
0
    vty_out(vty, "<null zclient>\n");
487
0
  }
488
0
}
489
490
int pim_zlookup_sg_statistics(struct channel_oil *c_oil)
491
0
{
492
0
  struct stream *s = zlookup->obuf;
493
0
  uint16_t command = 0;
494
0
  unsigned long long lastused;
495
0
  pim_sgaddr sg;
496
0
  int count = 0;
497
0
  int ret;
498
0
  pim_sgaddr more = {};
499
0
  struct interface *ifp =
500
0
    pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil));
501
502
0
  if (PIM_DEBUG_ZEBRA) {
503
0
    more.src = *oil_origin(c_oil);
504
0
    more.grp = *oil_mcastgrp(c_oil);
505
0
    zlog_debug(
506
0
      "Sending Request for New Channel Oil Information%pSG VIIF %d(%s)",
507
0
      &more, *oil_parent(c_oil), c_oil->pim->vrf->name);
508
0
  }
509
510
0
  if (!ifp)
511
0
    return -1;
512
513
0
  stream_reset(s);
514
0
  zclient_create_header(s, ZEBRA_IPMR_ROUTE_STATS,
515
0
            c_oil->pim->vrf->vrf_id);
516
0
  stream_putl(s, PIM_AF);
517
0
  stream_write(s, oil_origin(c_oil), sizeof(pim_addr));
518
0
  stream_write(s, oil_mcastgrp(c_oil), sizeof(pim_addr));
519
0
  stream_putl(s, ifp->ifindex);
520
0
  stream_putw_at(s, 0, stream_get_endp(s));
521
522
0
  count = stream_get_endp(s);
523
0
  ret = writen(zlookup->sock, s->data, count);
524
0
  if (ret <= 0) {
525
0
    flog_err(
526
0
      EC_LIB_SOCKET,
527
0
      "%s: writen() failure: %d writing to zclient lookup socket",
528
0
      __func__, errno);
529
0
    return -1;
530
0
  }
531
532
0
  s = zlookup->ibuf;
533
534
0
  while (command != ZEBRA_IPMR_ROUTE_STATS) {
535
0
    int err;
536
0
    uint16_t length = 0;
537
0
    vrf_id_t vrf_id;
538
0
    uint8_t marker;
539
0
    uint8_t version;
540
541
0
    stream_reset(s);
542
0
    err = zclient_read_header(s, zlookup->sock, &length, &marker,
543
0
            &version, &vrf_id, &command);
544
0
    if (err < 0) {
545
0
      flog_err(EC_LIB_ZAPI_MISSMATCH,
546
0
         "%s: zclient_read_header() failed", __func__);
547
0
      zclient_lookup_failed(zlookup);
548
0
      return -1;
549
0
    }
550
0
  }
551
552
0
  stream_get(&sg.src, s, sizeof(pim_addr));
553
0
  stream_get(&sg.grp, s, sizeof(pim_addr));
554
555
0
  more.src = *oil_origin(c_oil);
556
0
  more.grp = *oil_mcastgrp(c_oil);
557
0
  if (pim_sgaddr_cmp(sg, more)) {
558
0
    if (PIM_DEBUG_ZEBRA)
559
0
      flog_err(
560
0
        EC_LIB_ZAPI_MISSMATCH,
561
0
        "%s: Received wrong %pSG(%s) information requested",
562
0
        __func__, &more, c_oil->pim->vrf->name);
563
0
    zclient_lookup_failed(zlookup);
564
0
    return -3;
565
0
  }
566
567
0
  stream_get(&lastused, s, sizeof(lastused));
568
  /* signed success value from netlink_talk; currently unused */
569
0
  (void)stream_getl(s);
570
571
0
  c_oil->cc.lastused = lastused;
572
573
0
  return 0;
574
0
}