Coverage Report

Created: 2025-08-28 06:29

/src/frr/zebra/zebra_pw.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* Zebra PW code
3
 * Copyright (C) 2016 Volta Networks, Inc.
4
 */
5
6
#include <zebra.h>
7
8
#include "log.h"
9
#include "memory.h"
10
#include "frrevent.h"
11
#include "command.h"
12
#include "vrf.h"
13
#include "lib/json.h"
14
#include "printfrr.h"
15
16
#include "zebra/debug.h"
17
#include "zebra/rib.h"
18
#include "zebra/zebra_router.h"
19
#include "zebra/zapi_msg.h"
20
#include "zebra/zebra_rnh.h"
21
#include "zebra/zebra_vrf.h"
22
#include "zebra/zebra_pw.h"
23
24
DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire");
25
26
DEFINE_QOBJ_TYPE(zebra_pw);
27
28
DEFINE_HOOK(pw_install, (struct zebra_pw * pw), (pw));
29
DEFINE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw));
30
31
0
#define MPLS_NO_LABEL MPLS_INVALID_LABEL
32
33
static int zebra_pw_enabled(struct zebra_pw *);
34
static void zebra_pw_install(struct zebra_pw *);
35
static void zebra_pw_uninstall(struct zebra_pw *);
36
static void zebra_pw_install_retry(struct event *thread);
37
static int zebra_pw_check_reachability(const struct zebra_pw *);
38
static void zebra_pw_update_status(struct zebra_pw *, int);
39
40
static inline int zebra_pw_compare(const struct zebra_pw *a,
41
           const struct zebra_pw *b)
42
0
{
43
0
  return (strcmp(a->ifname, b->ifname));
44
0
}
45
46
RB_GENERATE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare)
47
RB_GENERATE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare)
48
49
struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname,
50
            uint8_t protocol, struct zserv *client)
51
0
{
52
0
  struct zebra_pw *pw;
53
54
0
  if (IS_ZEBRA_DEBUG_PW)
55
0
    zlog_debug("%u: adding pseudowire %s protocol %s",
56
0
         zvrf_id(zvrf), ifname, zebra_route_string(protocol));
57
58
0
  pw = XCALLOC(MTYPE_PW, sizeof(*pw));
59
0
  strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
60
0
  pw->protocol = protocol;
61
0
  pw->vrf_id = zvrf_id(zvrf);
62
0
  pw->client = client;
63
0
  pw->status = PW_NOT_FORWARDING;
64
0
  pw->local_label = MPLS_NO_LABEL;
65
0
  pw->remote_label = MPLS_NO_LABEL;
66
0
  pw->flags = F_PSEUDOWIRE_CWORD;
67
68
0
  RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw);
69
0
  if (pw->protocol == ZEBRA_ROUTE_STATIC) {
70
0
    RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw);
71
0
    QOBJ_REG(pw, zebra_pw);
72
0
  }
73
74
0
  return pw;
75
0
}
76
77
void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw)
78
0
{
79
0
  if (IS_ZEBRA_DEBUG_PW)
80
0
    zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id,
81
0
         pw->ifname, zebra_route_string(pw->protocol));
82
83
  /* remove nexthop tracking */
84
0
  zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
85
86
  /* uninstall */
87
0
  if (pw->status == PW_FORWARDING) {
88
0
    hook_call(pw_uninstall, pw);
89
0
    dplane_pw_uninstall(pw);
90
0
  }
91
92
0
  EVENT_OFF(pw->install_retry_timer);
93
94
  /* unlink and release memory */
95
0
  RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw);
96
0
  if (pw->protocol == ZEBRA_ROUTE_STATIC)
97
0
    RB_REMOVE(zebra_static_pw_head, &zvrf->static_pseudowires, pw);
98
99
0
  XFREE(MTYPE_PW, pw);
100
0
}
101
102
void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af,
103
         union g_addr *nexthop, uint32_t local_label,
104
         uint32_t remote_label, uint8_t flags,
105
         union pw_protocol_fields *data)
106
0
{
107
0
  pw->ifindex = ifindex;
108
0
  pw->type = type;
109
0
  pw->af = af;
110
0
  pw->nexthop = *nexthop;
111
0
  pw->local_label = local_label;
112
0
  pw->remote_label = remote_label;
113
0
  pw->flags = flags;
114
0
  pw->data = *data;
115
116
0
  if (zebra_pw_enabled(pw)) {
117
0
    bool nht_exists;
118
0
    zebra_register_rnh_pseudowire(pw->vrf_id, pw, &nht_exists);
119
0
    if (nht_exists)
120
0
      zebra_pw_update(pw);
121
0
  } else {
122
0
    if (pw->protocol == ZEBRA_ROUTE_STATIC)
123
0
      zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
124
0
    zebra_pw_uninstall(pw);
125
0
  }
126
0
}
127
128
struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname)
129
0
{
130
0
  struct zebra_pw pw;
131
0
  strlcpy(pw.ifname, ifname, sizeof(pw.ifname));
132
0
  return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw));
133
0
}
134
135
static int zebra_pw_enabled(struct zebra_pw *pw)
136
0
{
137
0
  if (pw->protocol == ZEBRA_ROUTE_STATIC) {
138
0
    if (pw->local_label == MPLS_NO_LABEL
139
0
        || pw->remote_label == MPLS_NO_LABEL || pw->af == AF_UNSPEC)
140
0
      return 0;
141
0
    return 1;
142
0
  } else
143
0
    return pw->enabled;
144
0
}
145
146
void zebra_pw_update(struct zebra_pw *pw)
147
0
{
148
0
  if (zebra_pw_check_reachability(pw) < 0) {
149
0
    zebra_pw_uninstall(pw);
150
0
    zebra_pw_install_failure(pw, PW_NOT_FORWARDING);
151
    /* wait for NHT and try again later */
152
0
  } else {
153
    /*
154
     * Install or reinstall the pseudowire (e.g. to update
155
     * parameters like the nexthop or the use of the control word).
156
     */
157
0
    zebra_pw_install(pw);
158
0
  }
159
0
}
160
161
static void zebra_pw_install(struct zebra_pw *pw)
162
0
{
163
0
  if (IS_ZEBRA_DEBUG_PW)
164
0
    zlog_debug("%u: installing pseudowire %s protocol %s",
165
0
         pw->vrf_id, pw->ifname,
166
0
         zebra_route_string(pw->protocol));
167
168
0
  hook_call(pw_install, pw);
169
0
  if (dplane_pw_install(pw) == ZEBRA_DPLANE_REQUEST_FAILURE) {
170
0
    zebra_pw_install_failure(pw, PW_NOT_FORWARDING);
171
0
    return;
172
0
  }
173
174
0
  if (pw->status != PW_FORWARDING)
175
0
    zebra_pw_update_status(pw, PW_FORWARDING);
176
0
}
177
178
static void zebra_pw_uninstall(struct zebra_pw *pw)
179
0
{
180
0
  if (pw->status != PW_FORWARDING)
181
0
    return;
182
183
0
  if (IS_ZEBRA_DEBUG_PW)
184
0
    zlog_debug("%u: uninstalling pseudowire %s protocol %s",
185
0
         pw->vrf_id, pw->ifname,
186
0
         zebra_route_string(pw->protocol));
187
188
  /* ignore any possible error */
189
0
  hook_call(pw_uninstall, pw);
190
0
  dplane_pw_uninstall(pw);
191
192
0
  if (zebra_pw_enabled(pw))
193
0
    zebra_pw_update_status(pw, PW_NOT_FORWARDING);
194
0
}
195
196
/*
197
 * Installation of the pseudowire in the kernel or hardware has failed. This
198
 * function will notify the pseudowire client about the failure and schedule
199
 * to retry the installation later. This function can be called by an external
200
 * agent that performs the pseudowire installation in an asynchronous way.
201
 */
202
void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus)
203
0
{
204
0
  if (IS_ZEBRA_DEBUG_PW)
205
0
    zlog_debug(
206
0
      "%u: failed installing pseudowire %s, scheduling retry in %u seconds",
207
0
      pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL);
208
209
  /* schedule to retry later */
210
0
  EVENT_OFF(pw->install_retry_timer);
211
0
  event_add_timer(zrouter.master, zebra_pw_install_retry, pw,
212
0
      PW_INSTALL_RETRY_INTERVAL, &pw->install_retry_timer);
213
214
0
  zebra_pw_update_status(pw, pwstatus);
215
0
}
216
217
static void zebra_pw_install_retry(struct event *thread)
218
0
{
219
0
  struct zebra_pw *pw = EVENT_ARG(thread);
220
0
221
0
  zebra_pw_install(pw);
222
0
}
223
224
static void zebra_pw_update_status(struct zebra_pw *pw, int status)
225
0
{
226
0
  pw->status = status;
227
0
  if (pw->client)
228
0
    zsend_pw_update(pw->client, pw);
229
0
}
230
231
static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw,
232
                struct route_entry *re)
233
0
{
234
0
  const struct nexthop *nexthop;
235
0
  const struct nexthop_group *nhg;
236
0
  bool found_p = false;
237
0
  bool fail_p = false;
238
239
  /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
240
241
  /* All active nexthops must be labelled; look at
242
   * primary and backup fib lists, in case there's been
243
   * a backup nexthop activation.
244
   */
245
0
  nhg = rib_get_fib_nhg(re);
246
0
  if (nhg && nhg->nexthop) {
247
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
248
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
249
0
        continue;
250
251
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
252
0
        if (nexthop->nh_label != NULL)
253
0
          found_p = true;
254
0
        else {
255
0
          fail_p = true;
256
0
          break;
257
0
        }
258
0
      }
259
0
    }
260
0
  }
261
262
0
  if (fail_p)
263
0
    goto done;
264
265
0
  nhg = rib_get_fib_backup_nhg(re);
266
0
  if (nhg && nhg->nexthop) {
267
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
268
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
269
0
        continue;
270
271
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
272
0
        if (nexthop->nh_label != NULL)
273
0
          found_p = true;
274
0
        else {
275
0
          fail_p = true;
276
0
          break;
277
0
        }
278
0
      }
279
0
    }
280
0
  }
281
282
0
done:
283
284
0
  if (fail_p || !found_p) {
285
0
    if (IS_ZEBRA_DEBUG_PW)
286
0
      zlog_debug("%s: unlabeled route for %s",
287
0
           __func__, pw->ifname);
288
0
    return -1;
289
0
  }
290
291
0
  return 0;
292
0
}
293
294
static int zebra_pw_check_reachability(const struct zebra_pw *pw)
295
0
{
296
0
  struct route_entry *re;
297
0
  const struct nexthop *nexthop;
298
0
  const struct nexthop_group *nhg;
299
0
  bool found_p = false;
300
301
  /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
302
303
  /* Find route to the remote end of the pseudowire */
304
0
  re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
305
0
           &pw->nexthop, NULL);
306
0
  if (!re) {
307
0
    if (IS_ZEBRA_DEBUG_PW)
308
0
      zlog_debug("%s: no route found for %s", __func__,
309
0
           pw->ifname);
310
0
    return -1;
311
0
  }
312
313
  /* Stricter checking for some OSes (OBSD, e.g.) */
314
0
  if (mpls_pw_reach_strict)
315
0
    return zebra_pw_check_reachability_strict(pw, re);
316
317
  /* There must be at least one installed labelled nexthop;
318
   * look at primary and backup fib lists, in case there's been
319
   * a backup nexthop activation.
320
   */
321
0
  nhg = rib_get_fib_nhg(re);
322
0
  if (nhg && nhg->nexthop) {
323
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
324
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
325
0
        continue;
326
327
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
328
0
          nexthop->nh_label != NULL) {
329
0
        found_p = true;
330
0
        break;
331
0
      }
332
0
    }
333
0
  }
334
335
0
  if (found_p)
336
0
    return 0;
337
338
0
  nhg = rib_get_fib_backup_nhg(re);
339
0
  if (nhg && nhg->nexthop) {
340
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
341
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
342
0
        continue;
343
344
0
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
345
0
          nexthop->nh_label != NULL) {
346
0
        found_p = true;
347
0
        break;
348
0
      }
349
0
    }
350
0
  }
351
352
0
  if (!found_p) {
353
0
    if (IS_ZEBRA_DEBUG_PW)
354
0
      zlog_debug("%s: unlabeled route for %s",
355
0
           __func__, pw->ifname);
356
0
    return -1;
357
0
  }
358
359
0
  return 0;
360
0
}
361
362
static int zebra_pw_client_close(struct zserv *client)
363
0
{
364
0
  struct vrf *vrf;
365
0
  struct zebra_vrf *zvrf;
366
0
  struct zebra_pw *pw, *tmp;
367
368
0
  RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
369
0
    zvrf = vrf->info;
370
0
    RB_FOREACH_SAFE (pw, zebra_pw_head, &zvrf->pseudowires, tmp) {
371
0
      if (pw->client != client)
372
0
        continue;
373
0
      zebra_pw_del(zvrf, pw);
374
0
    }
375
0
  }
376
377
0
  return 0;
378
0
}
379
380
void zebra_pw_init(struct zebra_vrf *zvrf)
381
0
{
382
0
  RB_INIT(zebra_pw_head, &zvrf->pseudowires);
383
0
  RB_INIT(zebra_static_pw_head, &zvrf->static_pseudowires);
384
385
0
  hook_register(zserv_client_close, zebra_pw_client_close);
386
0
}
387
388
void zebra_pw_exit(struct zebra_vrf *zvrf)
389
0
{
390
0
  struct zebra_pw *pw;
391
392
0
  while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) {
393
0
    pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires);
394
395
0
    zebra_pw_del(zvrf, pw);
396
0
  }
397
0
}
398
399
DEFUN_NOSH (pseudowire_if,
400
      pseudowire_if_cmd,
401
      "pseudowire IFNAME",
402
      "Static pseudowire configuration\n"
403
      "Pseudowire name\n")
404
0
{
405
0
  struct zebra_vrf *zvrf;
406
0
  struct zebra_pw *pw;
407
0
  const char *ifname;
408
0
  int idx = 0;
409
410
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
411
0
  if (!zvrf)
412
0
    return CMD_WARNING;
413
414
0
  argv_find(argv, argc, "IFNAME", &idx);
415
0
  ifname = argv[idx]->arg;
416
417
0
  pw = zebra_pw_find(zvrf, ifname);
418
0
  if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) {
419
0
    vty_out(vty, "%% Pseudowire is not static\n");
420
0
    return CMD_WARNING;
421
0
  }
422
423
0
  if (!pw)
424
0
    pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL);
425
0
  VTY_PUSH_CONTEXT(PW_NODE, pw);
426
427
0
  return CMD_SUCCESS;
428
0
}
429
430
DEFUN (no_pseudowire_if,
431
       no_pseudowire_if_cmd,
432
       "no pseudowire IFNAME",
433
       NO_STR
434
       "Static pseudowire configuration\n"
435
       "Pseudowire name\n")
436
0
{
437
0
  struct zebra_vrf *zvrf;
438
0
  struct zebra_pw *pw;
439
0
  const char *ifname;
440
0
  int idx = 0;
441
442
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
443
0
  if (!zvrf)
444
0
    return CMD_WARNING;
445
446
0
  argv_find(argv, argc, "IFNAME", &idx);
447
0
  ifname = argv[idx]->arg;
448
449
0
  pw = zebra_pw_find(zvrf, ifname);
450
0
  if (pw) {
451
0
    if (pw->protocol != ZEBRA_ROUTE_STATIC) {
452
0
      vty_out(vty, "%% Pseudowire is not static\n");
453
0
      return CMD_WARNING;
454
0
    }
455
0
    zebra_pw_del(zvrf, pw);
456
0
  }
457
458
0
  return CMD_SUCCESS;
459
0
}
460
461
DEFUN (pseudowire_labels,
462
       pseudowire_labels_cmd,
463
       "[no] mpls label local (16-1048575) remote (16-1048575)",
464
       NO_STR
465
       "MPLS L2VPN PW command\n"
466
       "MPLS L2VPN static labels\n"
467
       "Local pseudowire label\n"
468
       "Local pseudowire label\n"
469
       "Remote pseudowire label\n"
470
       "Remote pseudowire label\n")
471
0
{
472
0
  VTY_DECLVAR_CONTEXT(zebra_pw, pw);
473
0
  int idx = 0;
474
0
  mpls_label_t local_label, remote_label;
475
476
0
  if (argv_find(argv, argc, "no", &idx)) {
477
0
    local_label = MPLS_NO_LABEL;
478
0
    remote_label = MPLS_NO_LABEL;
479
0
  } else {
480
0
    argv_find(argv, argc, "local", &idx);
481
0
    local_label = atoi(argv[idx + 1]->arg);
482
0
    argv_find(argv, argc, "remote", &idx);
483
0
    remote_label = atoi(argv[idx + 1]->arg);
484
0
  }
485
486
0
  zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
487
0
      local_label, remote_label, pw->flags, &pw->data);
488
489
0
  return CMD_SUCCESS;
490
0
}
491
492
DEFUN (pseudowire_neighbor,
493
       pseudowire_neighbor_cmd,
494
       "[no] neighbor <A.B.C.D|X:X::X:X>",
495
       NO_STR
496
       "Specify the IPv4 or IPv6 address of the remote endpoint\n"
497
       "IPv4 address\n"
498
       "IPv6 address\n")
499
0
{
500
0
  VTY_DECLVAR_CONTEXT(zebra_pw, pw);
501
0
  int idx = 0;
502
0
  const char *address;
503
0
  int af;
504
0
  union g_addr nexthop;
505
506
0
  af = AF_UNSPEC;
507
0
  memset(&nexthop, 0, sizeof(nexthop));
508
509
0
  if (!argv_find(argv, argc, "no", &idx)) {
510
0
    argv_find(argv, argc, "neighbor", &idx);
511
0
    address = argv[idx + 1]->arg;
512
513
0
    if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1)
514
0
      af = AF_INET;
515
0
    else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1)
516
0
      af = AF_INET6;
517
0
    else {
518
0
      vty_out(vty, "%% Malformed address\n");
519
0
      return CMD_WARNING;
520
0
    }
521
0
  }
522
523
0
  zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop,
524
0
      pw->local_label, pw->remote_label, pw->flags,
525
0
      &pw->data);
526
527
0
  return CMD_SUCCESS;
528
0
}
529
530
DEFUN (pseudowire_control_word,
531
       pseudowire_control_word_cmd,
532
       "[no] control-word <exclude|include>",
533
       NO_STR
534
       "Control-word options\n"
535
       "Exclude control-word in pseudowire packets\n"
536
       "Include control-word in pseudowire packets\n")
537
0
{
538
0
  VTY_DECLVAR_CONTEXT(zebra_pw, pw);
539
0
  int idx = 0;
540
0
  uint8_t flags = 0;
541
542
0
  if (argv_find(argv, argc, "no", &idx))
543
0
    flags = F_PSEUDOWIRE_CWORD;
544
0
  else {
545
0
    argv_find(argv, argc, "control-word", &idx);
546
0
    if (argv[idx + 1]->text[0] == 'i')
547
0
      flags = F_PSEUDOWIRE_CWORD;
548
0
  }
549
550
0
  zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
551
0
      pw->local_label, pw->remote_label, flags, &pw->data);
552
553
0
  return CMD_SUCCESS;
554
0
}
555
556
DEFUN (show_pseudowires,
557
       show_pseudowires_cmd,
558
       "show mpls pseudowires",
559
       SHOW_STR
560
       MPLS_STR
561
       "Pseudowires\n")
562
0
{
563
0
  struct zebra_vrf *zvrf;
564
0
  struct zebra_pw *pw;
565
566
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
567
0
  if (!zvrf)
568
0
    return 0;
569
570
0
  vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", "Interface", "Neighbor",
571
0
    "Labels", "Protocol", "Status");
572
573
0
  RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
574
0
    char buf_nbr[INET6_ADDRSTRLEN];
575
0
    char buf_labels[64];
576
577
0
    inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
578
579
0
    if (pw->local_label != MPLS_NO_LABEL
580
0
        && pw->remote_label != MPLS_NO_LABEL)
581
0
      snprintf(buf_labels, sizeof(buf_labels), "%u/%u",
582
0
         pw->local_label, pw->remote_label);
583
0
    else
584
0
      snprintf(buf_labels, sizeof(buf_labels), "-");
585
586
0
    vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", pw->ifname,
587
0
      (pw->af != AF_UNSPEC) ? buf_nbr : "-", buf_labels,
588
0
      zebra_route_string(pw->protocol),
589
0
      (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
590
0
        ? "UP"
591
0
        : "DOWN");
592
0
  }
593
594
0
  return CMD_SUCCESS;
595
0
}
596
597
static void vty_show_mpls_pseudowire_detail(struct vty *vty)
598
0
{
599
0
  struct zebra_vrf *zvrf;
600
0
  struct zebra_pw *pw;
601
0
  struct route_entry *re;
602
0
  struct nexthop *nexthop;
603
0
  struct nexthop_group *nhg;
604
605
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
606
0
  if (!zvrf)
607
0
    return;
608
609
0
  RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
610
0
    char buf_nbr[INET6_ADDRSTRLEN];
611
0
    char buf_nh[100];
612
613
0
    vty_out(vty, "Interface: %s\n", pw->ifname);
614
0
    inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
615
0
    vty_out(vty, "  Neighbor: %s\n",
616
0
      (pw->af != AF_UNSPEC) ? buf_nbr : "-");
617
0
    if (pw->local_label != MPLS_NO_LABEL)
618
0
      vty_out(vty, "  Local Label: %u\n", pw->local_label);
619
0
    else
620
0
      vty_out(vty, "  Local Label: %s\n", "-");
621
0
    if (pw->remote_label != MPLS_NO_LABEL)
622
0
      vty_out(vty, "  Remote Label: %u\n", pw->remote_label);
623
0
    else
624
0
      vty_out(vty, "  Remote Label: %s\n", "-");
625
0
    vty_out(vty, "  Protocol: %s\n",
626
0
      zebra_route_string(pw->protocol));
627
0
    if (pw->protocol == ZEBRA_ROUTE_LDP)
628
0
      vty_out(vty, "  VC-ID: %u\n", pw->data.ldp.pwid);
629
0
    vty_out(vty, "  Status: %s \n",
630
0
      (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
631
0
      ? "Up"
632
0
      : "Down");
633
0
    re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
634
0
             &pw->nexthop, NULL);
635
0
    if (re == NULL)
636
0
      continue;
637
638
0
    nhg = rib_get_fib_nhg(re);
639
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
640
0
      snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
641
0
           nexthop);
642
0
      vty_out(vty, "  Next Hop: %s\n", buf_nh);
643
0
      if (nexthop->nh_label)
644
0
        vty_out(vty, "  Next Hop label: %u\n",
645
0
          nexthop->nh_label->label[0]);
646
0
      else
647
0
        vty_out(vty, "  Next Hop label: %s\n",
648
0
          "-");
649
0
    }
650
651
    /* Include any installed backups */
652
0
    nhg = rib_get_fib_backup_nhg(re);
653
0
    if (nhg == NULL)
654
0
      continue;
655
656
0
    for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
657
0
      snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
658
0
           nexthop);
659
0
      vty_out(vty, "  Next Hop: %s\n", buf_nh);
660
0
      if (nexthop->nh_label)
661
0
        vty_out(vty, "  Next Hop label: %u\n",
662
0
          nexthop->nh_label->label[0]);
663
0
      else
664
0
        vty_out(vty, "  Next Hop label: %s\n",
665
0
          "-");
666
0
    }
667
0
  }
668
0
}
669
670
static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
671
0
{
672
0
  struct route_entry *re;
673
0
  struct nexthop *nexthop;
674
0
  struct nexthop_group *nhg;
675
0
  char buf_nbr[INET6_ADDRSTRLEN];
676
0
  char buf_nh[100];
677
0
  json_object *json_pw = NULL;
678
0
  json_object *json_nexthop = NULL;
679
0
  json_object *json_nexthops = NULL;
680
681
0
  json_nexthops = json_object_new_array();
682
0
  json_pw = json_object_new_object();
683
684
0
  json_object_string_add(json_pw, "interface", pw->ifname);
685
0
  if (pw->af == AF_UNSPEC)
686
0
    json_object_string_add(json_pw, "neighbor", "-");
687
0
  else {
688
0
    inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
689
0
    json_object_string_add(json_pw, "neighbor", buf_nbr);
690
0
  }
691
0
  if (pw->local_label != MPLS_NO_LABEL)
692
0
    json_object_int_add(json_pw, "localLabel", pw->local_label);
693
0
  else
694
0
    json_object_string_add(json_pw, "localLabel", "-");
695
0
  if (pw->remote_label != MPLS_NO_LABEL)
696
0
    json_object_int_add(json_pw, "remoteLabel", pw->remote_label);
697
0
  else
698
0
    json_object_string_add(json_pw, "remoteLabel", "-");
699
0
  json_object_string_add(json_pw, "protocol",
700
0
             zebra_route_string(pw->protocol));
701
0
  if (pw->protocol == ZEBRA_ROUTE_LDP)
702
0
    json_object_int_add(json_pw, "vcId", pw->data.ldp.pwid);
703
0
  json_object_string_add(
704
0
    json_pw, "Status",
705
0
    (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) ? "Up"
706
0
                      : "Down");
707
0
  re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
708
0
           &pw->nexthop, NULL);
709
0
  if (re == NULL)
710
0
    goto done;
711
712
0
  nhg = rib_get_fib_nhg(re);
713
0
  for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
714
0
    json_nexthop = json_object_new_object();
715
0
    snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
716
0
    json_object_string_add(json_nexthop, "nexthop", buf_nh);
717
0
    if (nexthop->nh_label)
718
0
      json_object_int_add(
719
0
        json_nexthop, "nhLabel",
720
0
        nexthop->nh_label->label[0]);
721
0
    else
722
0
      json_object_string_add(json_nexthop, "nhLabel",
723
0
                 "-");
724
725
0
    json_object_array_add(json_nexthops, json_nexthop);
726
0
  }
727
728
  /* Include installed backup nexthops also */
729
0
  nhg = rib_get_fib_backup_nhg(re);
730
0
  if (nhg == NULL)
731
0
    goto done;
732
733
0
  for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
734
0
    json_nexthop = json_object_new_object();
735
0
    snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
736
0
    json_object_string_add(json_nexthop, "nexthop", buf_nh);
737
0
    if (nexthop->nh_label)
738
0
      json_object_int_add(
739
0
        json_nexthop, "nhLabel",
740
0
        nexthop->nh_label->label[0]);
741
0
    else
742
0
      json_object_string_add(json_nexthop, "nhLabel",
743
0
                 "-");
744
745
0
    json_object_array_add(json_nexthops, json_nexthop);
746
0
  }
747
748
0
done:
749
750
0
  json_object_object_add(json_pw, "nexthops", json_nexthops);
751
0
  json_object_array_add(json_pws, json_pw);
752
0
}
753
754
static void vty_show_mpls_pseudowire_detail_json(struct vty *vty)
755
0
{
756
0
  json_object *json = NULL;
757
0
  json_object *json_pws = NULL;
758
0
  struct zebra_vrf *zvrf;
759
0
  struct zebra_pw *pw;
760
761
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
762
0
  if (!zvrf)
763
0
    return;
764
765
0
  json = json_object_new_object();
766
0
  json_pws = json_object_new_array();
767
0
  RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
768
0
    vty_show_mpls_pseudowire(pw, json_pws);
769
0
  }
770
0
  json_object_object_add(json, "pw", json_pws);
771
0
  vty_json(vty, json);
772
0
}
773
774
DEFUN(show_pseudowires_detail, show_pseudowires_detail_cmd,
775
      "show mpls pseudowires detail [json]$json",
776
      SHOW_STR MPLS_STR
777
      "Pseudowires\n"
778
      "Detailed output\n" JSON_STR)
779
0
{
780
0
  bool uj = use_json(argc, argv);
781
782
0
  if (uj)
783
0
    vty_show_mpls_pseudowire_detail_json(vty);
784
0
  else
785
0
    vty_show_mpls_pseudowire_detail(vty);
786
787
0
  return CMD_SUCCESS;
788
0
}
789
790
/* Pseudowire configuration write function. */
791
static int zebra_pw_config(struct vty *vty)
792
0
{
793
0
  int write = 0;
794
0
  struct zebra_vrf *zvrf;
795
0
  struct zebra_pw *pw;
796
797
0
  zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
798
0
  if (!zvrf)
799
0
    return 0;
800
801
0
  RB_FOREACH (pw, zebra_static_pw_head, &zvrf->static_pseudowires) {
802
0
    vty_out(vty, "pseudowire %s\n", pw->ifname);
803
0
    if (pw->local_label != MPLS_NO_LABEL
804
0
        && pw->remote_label != MPLS_NO_LABEL)
805
0
      vty_out(vty, " mpls label local %u remote %u\n",
806
0
        pw->local_label, pw->remote_label);
807
0
    else
808
0
      vty_out(vty,
809
0
        " ! Incomplete config, specify the static MPLS labels\n");
810
811
0
    if (pw->af != AF_UNSPEC) {
812
0
      char buf[INET6_ADDRSTRLEN];
813
0
      inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf));
814
0
      vty_out(vty, " neighbor %s\n", buf);
815
0
    } else
816
0
      vty_out(vty,
817
0
        " ! Incomplete config, specify a neighbor address\n");
818
819
0
    if (!(pw->flags & F_PSEUDOWIRE_CWORD))
820
0
      vty_out(vty, " control-word exclude\n");
821
822
0
    vty_out(vty, "exit\n");
823
0
    vty_out(vty, "!\n");
824
0
    write = 1;
825
0
  }
826
827
0
  return write;
828
0
}
829
830
static int zebra_pw_config(struct vty *vty);
831
static struct cmd_node pw_node = {
832
  .name = "pw",
833
  .node = PW_NODE,
834
  .parent_node = CONFIG_NODE,
835
  .prompt = "%s(config-pw)# ",
836
  .config_write = zebra_pw_config,
837
};
838
839
void zebra_pw_vty_init(void)
840
0
{
841
0
  install_node(&pw_node);
842
0
  install_default(PW_NODE);
843
844
0
  install_element(CONFIG_NODE, &pseudowire_if_cmd);
845
0
  install_element(CONFIG_NODE, &no_pseudowire_if_cmd);
846
0
  install_element(PW_NODE, &pseudowire_labels_cmd);
847
0
  install_element(PW_NODE, &pseudowire_neighbor_cmd);
848
0
  install_element(PW_NODE, &pseudowire_control_word_cmd);
849
850
0
  install_element(VIEW_NODE, &show_pseudowires_cmd);
851
0
  install_element(VIEW_NODE, &show_pseudowires_detail_cmd);
852
0
}