]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_pw.c
bgpd: Refactor subgroup_announce_table() to reuse an existing helpers
[mirror_frr.git] / zebra / zebra_pw.c
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 #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 {
43 return (strcmp(a->ifname, b->ifname));
44 }
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 {
52 struct zebra_pw *pw;
53
54 if (IS_ZEBRA_DEBUG_PW)
55 zlog_debug("%u: adding pseudowire %s protocol %s",
56 zvrf_id(zvrf), ifname, zebra_route_string(protocol));
57
58 pw = XCALLOC(MTYPE_PW, sizeof(*pw));
59 strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
60 pw->protocol = protocol;
61 pw->vrf_id = zvrf_id(zvrf);
62 pw->client = client;
63 pw->status = PW_NOT_FORWARDING;
64 pw->local_label = MPLS_NO_LABEL;
65 pw->remote_label = MPLS_NO_LABEL;
66 pw->flags = F_PSEUDOWIRE_CWORD;
67
68 RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw);
69 if (pw->protocol == ZEBRA_ROUTE_STATIC) {
70 RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw);
71 QOBJ_REG(pw, zebra_pw);
72 }
73
74 return pw;
75 }
76
77 void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw)
78 {
79 if (IS_ZEBRA_DEBUG_PW)
80 zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id,
81 pw->ifname, zebra_route_string(pw->protocol));
82
83 /* remove nexthop tracking */
84 zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
85
86 /* uninstall */
87 if (pw->status == PW_FORWARDING) {
88 hook_call(pw_uninstall, pw);
89 dplane_pw_uninstall(pw);
90 }
91
92 EVENT_OFF(pw->install_retry_timer);
93
94 /* unlink and release memory */
95 RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw);
96 if (pw->protocol == ZEBRA_ROUTE_STATIC)
97 RB_REMOVE(zebra_static_pw_head, &zvrf->static_pseudowires, pw);
98
99 XFREE(MTYPE_PW, pw);
100 }
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 {
107 pw->ifindex = ifindex;
108 pw->type = type;
109 pw->af = af;
110 pw->nexthop = *nexthop;
111 pw->local_label = local_label;
112 pw->remote_label = remote_label;
113 pw->flags = flags;
114 pw->data = *data;
115
116 if (zebra_pw_enabled(pw)) {
117 bool nht_exists;
118 zebra_register_rnh_pseudowire(pw->vrf_id, pw, &nht_exists);
119 if (nht_exists)
120 zebra_pw_update(pw);
121 } else {
122 if (pw->protocol == ZEBRA_ROUTE_STATIC)
123 zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
124 zebra_pw_uninstall(pw);
125 }
126 }
127
128 struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname)
129 {
130 struct zebra_pw pw;
131 strlcpy(pw.ifname, ifname, sizeof(pw.ifname));
132 return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw));
133 }
134
135 static int zebra_pw_enabled(struct zebra_pw *pw)
136 {
137 if (pw->protocol == ZEBRA_ROUTE_STATIC) {
138 if (pw->local_label == MPLS_NO_LABEL
139 || pw->remote_label == MPLS_NO_LABEL || pw->af == AF_UNSPEC)
140 return 0;
141 return 1;
142 } else
143 return pw->enabled;
144 }
145
146 void zebra_pw_update(struct zebra_pw *pw)
147 {
148 if (zebra_pw_check_reachability(pw) < 0) {
149 zebra_pw_uninstall(pw);
150 zebra_pw_install_failure(pw, PW_NOT_FORWARDING);
151 /* wait for NHT and try again later */
152 } 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 zebra_pw_install(pw);
158 }
159 }
160
161 static void zebra_pw_install(struct zebra_pw *pw)
162 {
163 if (IS_ZEBRA_DEBUG_PW)
164 zlog_debug("%u: installing pseudowire %s protocol %s",
165 pw->vrf_id, pw->ifname,
166 zebra_route_string(pw->protocol));
167
168 hook_call(pw_install, pw);
169 if (dplane_pw_install(pw) == ZEBRA_DPLANE_REQUEST_FAILURE) {
170 zebra_pw_install_failure(pw, PW_NOT_FORWARDING);
171 return;
172 }
173
174 if (pw->status != PW_FORWARDING)
175 zebra_pw_update_status(pw, PW_FORWARDING);
176 }
177
178 static void zebra_pw_uninstall(struct zebra_pw *pw)
179 {
180 if (pw->status != PW_FORWARDING)
181 return;
182
183 if (IS_ZEBRA_DEBUG_PW)
184 zlog_debug("%u: uninstalling pseudowire %s protocol %s",
185 pw->vrf_id, pw->ifname,
186 zebra_route_string(pw->protocol));
187
188 /* ignore any possible error */
189 hook_call(pw_uninstall, pw);
190 dplane_pw_uninstall(pw);
191
192 if (zebra_pw_enabled(pw))
193 zebra_pw_update_status(pw, PW_NOT_FORWARDING);
194 }
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 {
204 if (IS_ZEBRA_DEBUG_PW)
205 zlog_debug(
206 "%u: failed installing pseudowire %s, scheduling retry in %u seconds",
207 pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL);
208
209 /* schedule to retry later */
210 EVENT_OFF(pw->install_retry_timer);
211 event_add_timer(zrouter.master, zebra_pw_install_retry, pw,
212 PW_INSTALL_RETRY_INTERVAL, &pw->install_retry_timer);
213
214 zebra_pw_update_status(pw, pwstatus);
215 }
216
217 static void zebra_pw_install_retry(struct event *thread)
218 {
219 struct zebra_pw *pw = EVENT_ARG(thread);
220
221 zebra_pw_install(pw);
222 }
223
224 static void zebra_pw_update_status(struct zebra_pw *pw, int status)
225 {
226 pw->status = status;
227 if (pw->client)
228 zsend_pw_update(pw->client, pw);
229 }
230
231 static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw,
232 struct route_entry *re)
233 {
234 const struct nexthop *nexthop;
235 const struct nexthop_group *nhg;
236 bool found_p = false;
237 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 nhg = rib_get_fib_nhg(re);
246 if (nhg && nhg->nexthop) {
247 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
248 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
249 continue;
250
251 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
252 if (nexthop->nh_label != NULL)
253 found_p = true;
254 else {
255 fail_p = true;
256 break;
257 }
258 }
259 }
260 }
261
262 if (fail_p)
263 goto done;
264
265 nhg = rib_get_fib_backup_nhg(re);
266 if (nhg && nhg->nexthop) {
267 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
268 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
269 continue;
270
271 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
272 if (nexthop->nh_label != NULL)
273 found_p = true;
274 else {
275 fail_p = true;
276 break;
277 }
278 }
279 }
280 }
281
282 done:
283
284 if (fail_p || !found_p) {
285 if (IS_ZEBRA_DEBUG_PW)
286 zlog_debug("%s: unlabeled route for %s",
287 __func__, pw->ifname);
288 return -1;
289 }
290
291 return 0;
292 }
293
294 static int zebra_pw_check_reachability(const struct zebra_pw *pw)
295 {
296 struct route_entry *re;
297 const struct nexthop *nexthop;
298 const struct nexthop_group *nhg;
299 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 re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
305 &pw->nexthop, NULL);
306 if (!re) {
307 if (IS_ZEBRA_DEBUG_PW)
308 zlog_debug("%s: no route found for %s", __func__,
309 pw->ifname);
310 return -1;
311 }
312
313 /* Stricter checking for some OSes (OBSD, e.g.) */
314 if (mpls_pw_reach_strict)
315 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 nhg = rib_get_fib_nhg(re);
322 if (nhg && nhg->nexthop) {
323 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
324 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
325 continue;
326
327 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
328 nexthop->nh_label != NULL) {
329 found_p = true;
330 break;
331 }
332 }
333 }
334
335 if (found_p)
336 return 0;
337
338 nhg = rib_get_fib_backup_nhg(re);
339 if (nhg && nhg->nexthop) {
340 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
341 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
342 continue;
343
344 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
345 nexthop->nh_label != NULL) {
346 found_p = true;
347 break;
348 }
349 }
350 }
351
352 if (!found_p) {
353 if (IS_ZEBRA_DEBUG_PW)
354 zlog_debug("%s: unlabeled route for %s",
355 __func__, pw->ifname);
356 return -1;
357 }
358
359 return 0;
360 }
361
362 static int zebra_pw_client_close(struct zserv *client)
363 {
364 struct vrf *vrf;
365 struct zebra_vrf *zvrf;
366 struct zebra_pw *pw, *tmp;
367
368 RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
369 zvrf = vrf->info;
370 RB_FOREACH_SAFE (pw, zebra_pw_head, &zvrf->pseudowires, tmp) {
371 if (pw->client != client)
372 continue;
373 zebra_pw_del(zvrf, pw);
374 }
375 }
376
377 return 0;
378 }
379
380 void zebra_pw_init(struct zebra_vrf *zvrf)
381 {
382 RB_INIT(zebra_pw_head, &zvrf->pseudowires);
383 RB_INIT(zebra_static_pw_head, &zvrf->static_pseudowires);
384
385 hook_register(zserv_client_close, zebra_pw_client_close);
386 }
387
388 void zebra_pw_exit(struct zebra_vrf *zvrf)
389 {
390 struct zebra_pw *pw;
391
392 while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) {
393 pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires);
394
395 zebra_pw_del(zvrf, pw);
396 }
397 }
398
399 DEFUN_NOSH (pseudowire_if,
400 pseudowire_if_cmd,
401 "pseudowire IFNAME",
402 "Static pseudowire configuration\n"
403 "Pseudowire name\n")
404 {
405 struct zebra_vrf *zvrf;
406 struct zebra_pw *pw;
407 const char *ifname;
408 int idx = 0;
409
410 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
411 if (!zvrf)
412 return CMD_WARNING;
413
414 argv_find(argv, argc, "IFNAME", &idx);
415 ifname = argv[idx]->arg;
416
417 pw = zebra_pw_find(zvrf, ifname);
418 if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) {
419 vty_out(vty, "%% Pseudowire is not static\n");
420 return CMD_WARNING;
421 }
422
423 if (!pw)
424 pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL);
425 VTY_PUSH_CONTEXT(PW_NODE, pw);
426
427 return CMD_SUCCESS;
428 }
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 {
437 struct zebra_vrf *zvrf;
438 struct zebra_pw *pw;
439 const char *ifname;
440 int idx = 0;
441
442 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
443 if (!zvrf)
444 return CMD_WARNING;
445
446 argv_find(argv, argc, "IFNAME", &idx);
447 ifname = argv[idx]->arg;
448
449 pw = zebra_pw_find(zvrf, ifname);
450 if (pw) {
451 if (pw->protocol != ZEBRA_ROUTE_STATIC) {
452 vty_out(vty, "%% Pseudowire is not static\n");
453 return CMD_WARNING;
454 }
455 zebra_pw_del(zvrf, pw);
456 }
457
458 return CMD_SUCCESS;
459 }
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 {
472 VTY_DECLVAR_CONTEXT(zebra_pw, pw);
473 int idx = 0;
474 mpls_label_t local_label, remote_label;
475
476 if (argv_find(argv, argc, "no", &idx)) {
477 local_label = MPLS_NO_LABEL;
478 remote_label = MPLS_NO_LABEL;
479 } else {
480 argv_find(argv, argc, "local", &idx);
481 local_label = atoi(argv[idx + 1]->arg);
482 argv_find(argv, argc, "remote", &idx);
483 remote_label = atoi(argv[idx + 1]->arg);
484 }
485
486 zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
487 local_label, remote_label, pw->flags, &pw->data);
488
489 return CMD_SUCCESS;
490 }
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 {
500 VTY_DECLVAR_CONTEXT(zebra_pw, pw);
501 int idx = 0;
502 const char *address;
503 int af;
504 union g_addr nexthop;
505
506 af = AF_UNSPEC;
507 memset(&nexthop, 0, sizeof(nexthop));
508
509 if (!argv_find(argv, argc, "no", &idx)) {
510 argv_find(argv, argc, "neighbor", &idx);
511 address = argv[idx + 1]->arg;
512
513 if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1)
514 af = AF_INET;
515 else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1)
516 af = AF_INET6;
517 else {
518 vty_out(vty, "%% Malformed address\n");
519 return CMD_WARNING;
520 }
521 }
522
523 zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop,
524 pw->local_label, pw->remote_label, pw->flags,
525 &pw->data);
526
527 return CMD_SUCCESS;
528 }
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 {
538 VTY_DECLVAR_CONTEXT(zebra_pw, pw);
539 int idx = 0;
540 uint8_t flags = 0;
541
542 if (argv_find(argv, argc, "no", &idx))
543 flags = F_PSEUDOWIRE_CWORD;
544 else {
545 argv_find(argv, argc, "control-word", &idx);
546 if (argv[idx + 1]->text[0] == 'i')
547 flags = F_PSEUDOWIRE_CWORD;
548 }
549
550 zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop,
551 pw->local_label, pw->remote_label, flags, &pw->data);
552
553 return CMD_SUCCESS;
554 }
555
556 DEFUN (show_pseudowires,
557 show_pseudowires_cmd,
558 "show mpls pseudowires",
559 SHOW_STR
560 MPLS_STR
561 "Pseudowires\n")
562 {
563 struct zebra_vrf *zvrf;
564 struct zebra_pw *pw;
565
566 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
567 if (!zvrf)
568 return 0;
569
570 vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", "Interface", "Neighbor",
571 "Labels", "Protocol", "Status");
572
573 RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
574 char buf_nbr[INET6_ADDRSTRLEN];
575 char buf_labels[64];
576
577 inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
578
579 if (pw->local_label != MPLS_NO_LABEL
580 && pw->remote_label != MPLS_NO_LABEL)
581 snprintf(buf_labels, sizeof(buf_labels), "%u/%u",
582 pw->local_label, pw->remote_label);
583 else
584 snprintf(buf_labels, sizeof(buf_labels), "-");
585
586 vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", pw->ifname,
587 (pw->af != AF_UNSPEC) ? buf_nbr : "-", buf_labels,
588 zebra_route_string(pw->protocol),
589 (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
590 ? "UP"
591 : "DOWN");
592 }
593
594 return CMD_SUCCESS;
595 }
596
597 static void vty_show_mpls_pseudowire_detail(struct vty *vty)
598 {
599 struct zebra_vrf *zvrf;
600 struct zebra_pw *pw;
601 struct route_entry *re;
602 struct nexthop *nexthop;
603 struct nexthop_group *nhg;
604
605 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
606 if (!zvrf)
607 return;
608
609 RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
610 char buf_nbr[INET6_ADDRSTRLEN];
611 char buf_nh[100];
612
613 vty_out(vty, "Interface: %s\n", pw->ifname);
614 inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
615 vty_out(vty, " Neighbor: %s\n",
616 (pw->af != AF_UNSPEC) ? buf_nbr : "-");
617 if (pw->local_label != MPLS_NO_LABEL)
618 vty_out(vty, " Local Label: %u\n", pw->local_label);
619 else
620 vty_out(vty, " Local Label: %s\n", "-");
621 if (pw->remote_label != MPLS_NO_LABEL)
622 vty_out(vty, " Remote Label: %u\n", pw->remote_label);
623 else
624 vty_out(vty, " Remote Label: %s\n", "-");
625 vty_out(vty, " Protocol: %s\n",
626 zebra_route_string(pw->protocol));
627 if (pw->protocol == ZEBRA_ROUTE_LDP)
628 vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid);
629 vty_out(vty, " Status: %s \n",
630 (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
631 ? "Up"
632 : "Down");
633 re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
634 &pw->nexthop, NULL);
635 if (re == NULL)
636 continue;
637
638 nhg = rib_get_fib_nhg(re);
639 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
640 snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
641 nexthop);
642 vty_out(vty, " Next Hop: %s\n", buf_nh);
643 if (nexthop->nh_label)
644 vty_out(vty, " Next Hop label: %u\n",
645 nexthop->nh_label->label[0]);
646 else
647 vty_out(vty, " Next Hop label: %s\n",
648 "-");
649 }
650
651 /* Include any installed backups */
652 nhg = rib_get_fib_backup_nhg(re);
653 if (nhg == NULL)
654 continue;
655
656 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
657 snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
658 nexthop);
659 vty_out(vty, " Next Hop: %s\n", buf_nh);
660 if (nexthop->nh_label)
661 vty_out(vty, " Next Hop label: %u\n",
662 nexthop->nh_label->label[0]);
663 else
664 vty_out(vty, " Next Hop label: %s\n",
665 "-");
666 }
667 }
668 }
669
670 static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
671 {
672 struct route_entry *re;
673 struct nexthop *nexthop;
674 struct nexthop_group *nhg;
675 char buf_nbr[INET6_ADDRSTRLEN];
676 char buf_nh[100];
677 json_object *json_pw = NULL;
678 json_object *json_nexthop = NULL;
679 json_object *json_nexthops = NULL;
680
681 json_nexthops = json_object_new_array();
682 json_pw = json_object_new_object();
683
684 json_object_string_add(json_pw, "interface", pw->ifname);
685 if (pw->af == AF_UNSPEC)
686 json_object_string_add(json_pw, "neighbor", "-");
687 else {
688 inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr));
689 json_object_string_add(json_pw, "neighbor", buf_nbr);
690 }
691 if (pw->local_label != MPLS_NO_LABEL)
692 json_object_int_add(json_pw, "localLabel", pw->local_label);
693 else
694 json_object_string_add(json_pw, "localLabel", "-");
695 if (pw->remote_label != MPLS_NO_LABEL)
696 json_object_int_add(json_pw, "remoteLabel", pw->remote_label);
697 else
698 json_object_string_add(json_pw, "remoteLabel", "-");
699 json_object_string_add(json_pw, "protocol",
700 zebra_route_string(pw->protocol));
701 if (pw->protocol == ZEBRA_ROUTE_LDP)
702 json_object_int_add(json_pw, "vcId", pw->data.ldp.pwid);
703 json_object_string_add(
704 json_pw, "Status",
705 (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) ? "Up"
706 : "Down");
707 re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
708 &pw->nexthop, NULL);
709 if (re == NULL)
710 goto done;
711
712 nhg = rib_get_fib_nhg(re);
713 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
714 json_nexthop = json_object_new_object();
715 snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
716 json_object_string_add(json_nexthop, "nexthop", buf_nh);
717 if (nexthop->nh_label)
718 json_object_int_add(
719 json_nexthop, "nhLabel",
720 nexthop->nh_label->label[0]);
721 else
722 json_object_string_add(json_nexthop, "nhLabel",
723 "-");
724
725 json_object_array_add(json_nexthops, json_nexthop);
726 }
727
728 /* Include installed backup nexthops also */
729 nhg = rib_get_fib_backup_nhg(re);
730 if (nhg == NULL)
731 goto done;
732
733 for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
734 json_nexthop = json_object_new_object();
735 snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
736 json_object_string_add(json_nexthop, "nexthop", buf_nh);
737 if (nexthop->nh_label)
738 json_object_int_add(
739 json_nexthop, "nhLabel",
740 nexthop->nh_label->label[0]);
741 else
742 json_object_string_add(json_nexthop, "nhLabel",
743 "-");
744
745 json_object_array_add(json_nexthops, json_nexthop);
746 }
747
748 done:
749
750 json_object_object_add(json_pw, "nexthops", json_nexthops);
751 json_object_array_add(json_pws, json_pw);
752 }
753
754 static void vty_show_mpls_pseudowire_detail_json(struct vty *vty)
755 {
756 json_object *json = NULL;
757 json_object *json_pws = NULL;
758 struct zebra_vrf *zvrf;
759 struct zebra_pw *pw;
760
761 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
762 if (!zvrf)
763 return;
764
765 json = json_object_new_object();
766 json_pws = json_object_new_array();
767 RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) {
768 vty_show_mpls_pseudowire(pw, json_pws);
769 }
770 json_object_object_add(json, "pw", json_pws);
771 vty_json(vty, json);
772 }
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 {
780 bool uj = use_json(argc, argv);
781
782 if (uj)
783 vty_show_mpls_pseudowire_detail_json(vty);
784 else
785 vty_show_mpls_pseudowire_detail(vty);
786
787 return CMD_SUCCESS;
788 }
789
790 /* Pseudowire configuration write function. */
791 static int zebra_pw_config(struct vty *vty)
792 {
793 int write = 0;
794 struct zebra_vrf *zvrf;
795 struct zebra_pw *pw;
796
797 zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
798 if (!zvrf)
799 return 0;
800
801 RB_FOREACH (pw, zebra_static_pw_head, &zvrf->static_pseudowires) {
802 vty_out(vty, "pseudowire %s\n", pw->ifname);
803 if (pw->local_label != MPLS_NO_LABEL
804 && pw->remote_label != MPLS_NO_LABEL)
805 vty_out(vty, " mpls label local %u remote %u\n",
806 pw->local_label, pw->remote_label);
807 else
808 vty_out(vty,
809 " ! Incomplete config, specify the static MPLS labels\n");
810
811 if (pw->af != AF_UNSPEC) {
812 char buf[INET6_ADDRSTRLEN];
813 inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf));
814 vty_out(vty, " neighbor %s\n", buf);
815 } else
816 vty_out(vty,
817 " ! Incomplete config, specify a neighbor address\n");
818
819 if (!(pw->flags & F_PSEUDOWIRE_CWORD))
820 vty_out(vty, " control-word exclude\n");
821
822 vty_out(vty, "exit\n");
823 vty_out(vty, "!\n");
824 write = 1;
825 }
826
827 return write;
828 }
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 {
841 install_node(&pw_node);
842 install_default(PW_NODE);
843
844 install_element(CONFIG_NODE, &pseudowire_if_cmd);
845 install_element(CONFIG_NODE, &no_pseudowire_if_cmd);
846 install_element(PW_NODE, &pseudowire_labels_cmd);
847 install_element(PW_NODE, &pseudowire_neighbor_cmd);
848 install_element(PW_NODE, &pseudowire_control_word_cmd);
849
850 install_element(VIEW_NODE, &show_pseudowires_cmd);
851 install_element(VIEW_NODE, &show_pseudowires_detail_cmd);
852 }