]>
Commit | Line | Data |
---|---|---|
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 | } |