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