]>
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 *); | |
cc9f21da | 50 | static void zebra_pw_install_retry(struct thread *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 | ||
cc9f21da | 229 | static void zebra_pw_install_retry(struct thread *thread) |
6833ae01 | 230 | { |
231 | struct zebra_pw *pw = THREAD_ARG(thread); | |
232 | ||
233 | pw->install_retry_timer = NULL; | |
234 | zebra_pw_install(pw); | |
6833ae01 | 235 | } |
236 | ||
237 | static void zebra_pw_update_status(struct zebra_pw *pw, int status) | |
238 | { | |
239 | pw->status = status; | |
240 | if (pw->client) | |
241 | zsend_pw_update(pw->client, pw); | |
242 | } | |
243 | ||
0d145d47 MS |
244 | static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw, |
245 | struct route_entry *re) | |
246 | { | |
247 | const struct nexthop *nexthop; | |
248 | const struct nexthop_group *nhg; | |
249 | bool found_p = false; | |
250 | bool fail_p = false; | |
251 | ||
252 | /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ | |
253 | ||
254 | /* All active nexthops must be labelled; look at | |
255 | * primary and backup fib lists, in case there's been | |
256 | * a backup nexthop activation. | |
257 | */ | |
258 | nhg = rib_get_fib_nhg(re); | |
259 | if (nhg && nhg->nexthop) { | |
260 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
261 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
262 | continue; | |
263 | ||
264 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { | |
265 | if (nexthop->nh_label != NULL) | |
266 | found_p = true; | |
267 | else { | |
268 | fail_p = true; | |
269 | break; | |
270 | } | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | if (fail_p) | |
276 | goto done; | |
277 | ||
278 | nhg = rib_get_fib_backup_nhg(re); | |
279 | if (nhg && nhg->nexthop) { | |
280 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
281 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
282 | continue; | |
283 | ||
284 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { | |
285 | if (nexthop->nh_label != NULL) | |
286 | found_p = true; | |
287 | else { | |
288 | fail_p = true; | |
289 | break; | |
290 | } | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | done: | |
296 | ||
297 | if (fail_p || !found_p) { | |
298 | if (IS_ZEBRA_DEBUG_PW) | |
299 | zlog_debug("%s: unlabeled route for %s", | |
300 | __func__, pw->ifname); | |
301 | return -1; | |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | static int zebra_pw_check_reachability(const struct zebra_pw *pw) | |
6833ae01 | 308 | { |
69965f53 | 309 | struct route_entry *re; |
0d145d47 MS |
310 | const struct nexthop *nexthop; |
311 | const struct nexthop_group *nhg; | |
312 | bool found_p = false; | |
6833ae01 | 313 | |
314 | /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ | |
315 | ||
0d145d47 | 316 | /* Find route to the remote end of the pseudowire */ |
69965f53 DL |
317 | re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, |
318 | &pw->nexthop, NULL); | |
319 | if (!re) { | |
6833ae01 | 320 | if (IS_ZEBRA_DEBUG_PW) |
9df414fe QY |
321 | zlog_debug("%s: no route found for %s", __func__, |
322 | pw->ifname); | |
6833ae01 | 323 | return -1; |
324 | } | |
325 | ||
0d145d47 MS |
326 | /* Stricter checking for some OSes (OBSD, e.g.) */ |
327 | if (mpls_pw_reach_strict) | |
328 | return zebra_pw_check_reachability_strict(pw, re); | |
329 | ||
330 | /* There must be at least one installed labelled nexthop; | |
331 | * look at primary and backup fib lists, in case there's been | |
332 | * a backup nexthop activation. | |
6833ae01 | 333 | */ |
0d145d47 MS |
334 | nhg = rib_get_fib_nhg(re); |
335 | if (nhg && nhg->nexthop) { | |
336 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
337 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
338 | continue; | |
339 | ||
340 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && | |
341 | nexthop->nh_label != NULL) { | |
342 | found_p = true; | |
343 | break; | |
344 | } | |
345 | } | |
346 | } | |
347 | ||
348 | if (found_p) | |
349 | return 0; | |
350 | ||
351 | nhg = rib_get_fib_backup_nhg(re); | |
352 | if (nhg && nhg->nexthop) { | |
353 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
354 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
355 | continue; | |
356 | ||
357 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) && | |
358 | nexthop->nh_label != NULL) { | |
359 | found_p = true; | |
360 | break; | |
361 | } | |
6833ae01 | 362 | } |
363 | } | |
364 | ||
0d145d47 MS |
365 | if (!found_p) { |
366 | if (IS_ZEBRA_DEBUG_PW) | |
367 | zlog_debug("%s: unlabeled route for %s", | |
368 | __func__, pw->ifname); | |
369 | return -1; | |
370 | } | |
371 | ||
6833ae01 | 372 | return 0; |
373 | } | |
374 | ||
453844ab | 375 | static int zebra_pw_client_close(struct zserv *client) |
6833ae01 | 376 | { |
377 | struct vrf *vrf; | |
378 | struct zebra_vrf *zvrf; | |
379 | struct zebra_pw *pw, *tmp; | |
380 | ||
a2addae8 | 381 | RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { |
6833ae01 | 382 | zvrf = vrf->info; |
a2addae8 | 383 | RB_FOREACH_SAFE (pw, zebra_pw_head, &zvrf->pseudowires, tmp) { |
6833ae01 | 384 | if (pw->client != client) |
385 | continue; | |
386 | zebra_pw_del(zvrf, pw); | |
387 | } | |
388 | } | |
453844ab QY |
389 | |
390 | return 0; | |
6833ae01 | 391 | } |
392 | ||
393 | void zebra_pw_init(struct zebra_vrf *zvrf) | |
394 | { | |
69965f53 DL |
395 | RB_INIT(zebra_pw_head, &zvrf->pseudowires); |
396 | RB_INIT(zebra_static_pw_head, &zvrf->static_pseudowires); | |
453844ab | 397 | |
21ccc0cf | 398 | hook_register(zserv_client_close, zebra_pw_client_close); |
6833ae01 | 399 | } |
400 | ||
401 | void zebra_pw_exit(struct zebra_vrf *zvrf) | |
402 | { | |
403 | struct zebra_pw *pw; | |
404 | ||
55cd0f61 DS |
405 | while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) { |
406 | pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires); | |
407 | ||
6833ae01 | 408 | zebra_pw_del(zvrf, pw); |
55cd0f61 | 409 | } |
6833ae01 | 410 | } |
2dd0d726 RW |
411 | |
412 | DEFUN_NOSH (pseudowire_if, | |
413 | pseudowire_if_cmd, | |
1e9d1183 | 414 | "pseudowire IFNAME", |
2dd0d726 RW |
415 | "Static pseudowire configuration\n" |
416 | "Pseudowire name\n") | |
417 | { | |
418 | struct zebra_vrf *zvrf; | |
419 | struct zebra_pw *pw; | |
2dd0d726 | 420 | const char *ifname; |
1e9d1183 | 421 | int idx = 0; |
2dd0d726 RW |
422 | |
423 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
424 | if (!zvrf) | |
425 | return CMD_WARNING; | |
426 | ||
427 | argv_find(argv, argc, "IFNAME", &idx); | |
428 | ifname = argv[idx]->arg; | |
1e9d1183 | 429 | |
2dd0d726 RW |
430 | pw = zebra_pw_find(zvrf, ifname); |
431 | if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) { | |
69965f53 | 432 | vty_out(vty, "%% Pseudowire is not static\n"); |
2dd0d726 RW |
433 | return CMD_WARNING; |
434 | } | |
435 | ||
2dd0d726 RW |
436 | if (!pw) |
437 | pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL); | |
438 | VTY_PUSH_CONTEXT(PW_NODE, pw); | |
439 | ||
440 | return CMD_SUCCESS; | |
441 | } | |
442 | ||
1e9d1183 RW |
443 | DEFUN (no_pseudowire_if, |
444 | no_pseudowire_if_cmd, | |
445 | "no pseudowire IFNAME", | |
446 | NO_STR | |
447 | "Static pseudowire configuration\n" | |
448 | "Pseudowire name\n") | |
449 | { | |
450 | struct zebra_vrf *zvrf; | |
451 | struct zebra_pw *pw; | |
452 | const char *ifname; | |
453 | int idx = 0; | |
454 | ||
455 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
456 | if (!zvrf) | |
457 | return CMD_WARNING; | |
458 | ||
459 | argv_find(argv, argc, "IFNAME", &idx); | |
460 | ifname = argv[idx]->arg; | |
461 | ||
462 | pw = zebra_pw_find(zvrf, ifname); | |
463 | if (pw) { | |
464 | if (pw->protocol != ZEBRA_ROUTE_STATIC) { | |
465 | vty_out(vty, "%% Pseudowire is not static\n"); | |
466 | return CMD_WARNING; | |
467 | } | |
468 | zebra_pw_del(zvrf, pw); | |
469 | } | |
470 | ||
471 | return CMD_SUCCESS; | |
472 | } | |
473 | ||
2dd0d726 RW |
474 | DEFUN (pseudowire_labels, |
475 | pseudowire_labels_cmd, | |
476 | "[no] mpls label local (16-1048575) remote (16-1048575)", | |
477 | NO_STR | |
478 | "MPLS L2VPN PW command\n" | |
479 | "MPLS L2VPN static labels\n" | |
480 | "Local pseudowire label\n" | |
481 | "Local pseudowire label\n" | |
482 | "Remote pseudowire label\n" | |
483 | "Remote pseudowire label\n") | |
484 | { | |
485 | VTY_DECLVAR_CONTEXT(zebra_pw, pw); | |
486 | int idx = 0; | |
487 | mpls_label_t local_label, remote_label; | |
488 | ||
489 | if (argv_find(argv, argc, "no", &idx)) { | |
490 | local_label = MPLS_NO_LABEL; | |
491 | remote_label = MPLS_NO_LABEL; | |
492 | } else { | |
493 | argv_find(argv, argc, "local", &idx); | |
494 | local_label = atoi(argv[idx + 1]->arg); | |
495 | argv_find(argv, argc, "remote", &idx); | |
496 | remote_label = atoi(argv[idx + 1]->arg); | |
497 | } | |
498 | ||
499 | zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, | |
500 | local_label, remote_label, pw->flags, &pw->data); | |
501 | ||
502 | return CMD_SUCCESS; | |
503 | } | |
504 | ||
505 | DEFUN (pseudowire_neighbor, | |
506 | pseudowire_neighbor_cmd, | |
507 | "[no] neighbor <A.B.C.D|X:X::X:X>", | |
508 | NO_STR | |
509 | "Specify the IPv4 or IPv6 address of the remote endpoint\n" | |
510 | "IPv4 address\n" | |
511 | "IPv6 address\n") | |
512 | { | |
513 | VTY_DECLVAR_CONTEXT(zebra_pw, pw); | |
514 | int idx = 0; | |
515 | const char *address; | |
516 | int af; | |
517 | union g_addr nexthop; | |
518 | ||
519 | af = AF_UNSPEC; | |
520 | memset(&nexthop, 0, sizeof(nexthop)); | |
521 | ||
522 | if (!argv_find(argv, argc, "no", &idx)) { | |
523 | argv_find(argv, argc, "neighbor", &idx); | |
524 | address = argv[idx + 1]->arg; | |
525 | ||
526 | if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1) | |
527 | af = AF_INET; | |
528 | else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1) | |
529 | af = AF_INET6; | |
530 | else { | |
69965f53 | 531 | vty_out(vty, "%% Malformed address\n"); |
2dd0d726 RW |
532 | return CMD_WARNING; |
533 | } | |
534 | } | |
535 | ||
536 | zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop, | |
537 | pw->local_label, pw->remote_label, pw->flags, | |
538 | &pw->data); | |
539 | ||
540 | return CMD_SUCCESS; | |
541 | } | |
542 | ||
543 | DEFUN (pseudowire_control_word, | |
544 | pseudowire_control_word_cmd, | |
545 | "[no] control-word <exclude|include>", | |
546 | NO_STR | |
547 | "Control-word options\n" | |
548 | "Exclude control-word in pseudowire packets\n" | |
549 | "Include control-word in pseudowire packets\n") | |
550 | { | |
551 | VTY_DECLVAR_CONTEXT(zebra_pw, pw); | |
552 | int idx = 0; | |
553 | uint8_t flags = 0; | |
554 | ||
555 | if (argv_find(argv, argc, "no", &idx)) | |
556 | flags = F_PSEUDOWIRE_CWORD; | |
557 | else { | |
558 | argv_find(argv, argc, "control-word", &idx); | |
559 | if (argv[idx + 1]->text[0] == 'i') | |
560 | flags = F_PSEUDOWIRE_CWORD; | |
561 | } | |
562 | ||
563 | zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, | |
564 | pw->local_label, pw->remote_label, flags, &pw->data); | |
565 | ||
566 | return CMD_SUCCESS; | |
567 | } | |
568 | ||
569 | DEFUN (show_pseudowires, | |
570 | show_pseudowires_cmd, | |
d261dd7e | 571 | "show mpls pseudowires", |
2dd0d726 | 572 | SHOW_STR |
d261dd7e | 573 | MPLS_STR |
efd7904e | 574 | "Pseudowires\n") |
2dd0d726 RW |
575 | { |
576 | struct zebra_vrf *zvrf; | |
577 | struct zebra_pw *pw; | |
578 | ||
579 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
580 | if (!zvrf) | |
581 | return 0; | |
582 | ||
69965f53 DL |
583 | vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", "Interface", "Neighbor", |
584 | "Labels", "Protocol", "Status"); | |
2dd0d726 | 585 | |
a2addae8 | 586 | RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { |
2dd0d726 RW |
587 | char buf_nbr[INET6_ADDRSTRLEN]; |
588 | char buf_labels[64]; | |
589 | ||
590 | inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); | |
591 | ||
592 | if (pw->local_label != MPLS_NO_LABEL | |
593 | && pw->remote_label != MPLS_NO_LABEL) | |
594 | snprintf(buf_labels, sizeof(buf_labels), "%u/%u", | |
595 | pw->local_label, pw->remote_label); | |
596 | else | |
597 | snprintf(buf_labels, sizeof(buf_labels), "-"); | |
598 | ||
69965f53 | 599 | vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", pw->ifname, |
2dd0d726 RW |
600 | (pw->af != AF_UNSPEC) ? buf_nbr : "-", buf_labels, |
601 | zebra_route_string(pw->protocol), | |
fd563cc7 | 602 | (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) |
2dd0d726 | 603 | ? "UP" |
69965f53 | 604 | : "DOWN"); |
2dd0d726 RW |
605 | } |
606 | ||
607 | return CMD_SUCCESS; | |
608 | } | |
609 | ||
6bbdd9e9 | 610 | static void vty_show_mpls_pseudowire_detail(struct vty *vty) |
611 | { | |
612 | struct zebra_vrf *zvrf; | |
613 | struct zebra_pw *pw; | |
614 | struct route_entry *re; | |
615 | struct nexthop *nexthop; | |
8b117ff0 | 616 | struct nexthop_group *nhg; |
6bbdd9e9 | 617 | |
618 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
619 | if (!zvrf) | |
620 | return; | |
621 | ||
622 | RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { | |
623 | char buf_nbr[INET6_ADDRSTRLEN]; | |
624 | char buf_nh[100]; | |
625 | ||
626 | vty_out(vty, "Interface: %s\n", pw->ifname); | |
627 | inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); | |
628 | vty_out(vty, " Neighbor: %s\n", | |
629 | (pw->af != AF_UNSPEC) ? buf_nbr : "-"); | |
630 | if (pw->local_label != MPLS_NO_LABEL) | |
631 | vty_out(vty, " Local Label: %u\n", pw->local_label); | |
632 | else | |
633 | vty_out(vty, " Local Label: %s\n", "-"); | |
634 | if (pw->remote_label != MPLS_NO_LABEL) | |
635 | vty_out(vty, " Remote Label: %u\n", pw->remote_label); | |
636 | else | |
637 | vty_out(vty, " Remote Label: %s\n", "-"); | |
638 | vty_out(vty, " Protocol: %s\n", | |
639 | zebra_route_string(pw->protocol)); | |
640 | if (pw->protocol == ZEBRA_ROUTE_LDP) | |
641 | vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid); | |
642 | vty_out(vty, " Status: %s \n", | |
fd563cc7 | 643 | (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) |
8b117ff0 MS |
644 | ? "Up" |
645 | : "Down"); | |
6bbdd9e9 | 646 | re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, |
647 | &pw->nexthop, NULL); | |
8b117ff0 MS |
648 | if (re == NULL) |
649 | continue; | |
650 | ||
651 | nhg = rib_get_fib_nhg(re); | |
652 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
653 | snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", | |
654 | nexthop); | |
655 | vty_out(vty, " Next Hop: %s\n", buf_nh); | |
656 | if (nexthop->nh_label) | |
657 | vty_out(vty, " Next Hop label: %u\n", | |
658 | nexthop->nh_label->label[0]); | |
659 | else | |
660 | vty_out(vty, " Next Hop label: %s\n", | |
661 | "-"); | |
662 | } | |
663 | ||
664 | /* Include any installed backups */ | |
665 | nhg = rib_get_fib_backup_nhg(re); | |
666 | if (nhg == NULL) | |
667 | continue; | |
668 | ||
669 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
670 | snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", | |
671 | nexthop); | |
672 | vty_out(vty, " Next Hop: %s\n", buf_nh); | |
673 | if (nexthop->nh_label) | |
674 | vty_out(vty, " Next Hop label: %u\n", | |
675 | nexthop->nh_label->label[0]); | |
676 | else | |
677 | vty_out(vty, " Next Hop label: %s\n", | |
678 | "-"); | |
6bbdd9e9 | 679 | } |
680 | } | |
681 | } | |
682 | ||
683 | static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) | |
684 | { | |
685 | struct route_entry *re; | |
686 | struct nexthop *nexthop; | |
8b117ff0 | 687 | struct nexthop_group *nhg; |
6bbdd9e9 | 688 | char buf_nbr[INET6_ADDRSTRLEN]; |
689 | char buf_nh[100]; | |
690 | json_object *json_pw = NULL; | |
691 | json_object *json_nexthop = NULL; | |
692 | json_object *json_nexthops = NULL; | |
693 | ||
694 | json_nexthops = json_object_new_array(); | |
695 | json_pw = json_object_new_object(); | |
696 | ||
697 | json_object_string_add(json_pw, "interface", pw->ifname); | |
698 | if (pw->af == AF_UNSPEC) | |
699 | json_object_string_add(json_pw, "neighbor", "-"); | |
700 | else { | |
701 | inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); | |
702 | json_object_string_add(json_pw, "neighbor", buf_nbr); | |
703 | } | |
704 | if (pw->local_label != MPLS_NO_LABEL) | |
705 | json_object_int_add(json_pw, "localLabel", pw->local_label); | |
706 | else | |
707 | json_object_string_add(json_pw, "localLabel", "-"); | |
708 | if (pw->remote_label != MPLS_NO_LABEL) | |
709 | json_object_int_add(json_pw, "remoteLabel", pw->remote_label); | |
710 | else | |
711 | json_object_string_add(json_pw, "remoteLabel", "-"); | |
712 | json_object_string_add(json_pw, "protocol", | |
713 | zebra_route_string(pw->protocol)); | |
714 | if (pw->protocol == ZEBRA_ROUTE_LDP) | |
715 | json_object_int_add(json_pw, "vcId", pw->data.ldp.pwid); | |
716 | json_object_string_add( | |
717 | json_pw, "Status", | |
fd563cc7 KS |
718 | (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) ? "Up" |
719 | : "Down"); | |
6bbdd9e9 | 720 | re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, |
721 | &pw->nexthop, NULL); | |
8b117ff0 MS |
722 | if (re == NULL) |
723 | goto done; | |
724 | ||
725 | nhg = rib_get_fib_nhg(re); | |
726 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
727 | json_nexthop = json_object_new_object(); | |
728 | snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); | |
729 | json_object_string_add(json_nexthop, "nexthop", buf_nh); | |
730 | if (nexthop->nh_label) | |
731 | json_object_int_add( | |
732 | json_nexthop, "nhLabel", | |
733 | nexthop->nh_label->label[0]); | |
734 | else | |
735 | json_object_string_add(json_nexthop, "nhLabel", | |
736 | "-"); | |
6bbdd9e9 | 737 | |
8b117ff0 MS |
738 | json_object_array_add(json_nexthops, json_nexthop); |
739 | } | |
740 | ||
741 | /* Include installed backup nexthops also */ | |
742 | nhg = rib_get_fib_backup_nhg(re); | |
743 | if (nhg == NULL) | |
744 | goto done; | |
745 | ||
746 | for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { | |
747 | json_nexthop = json_object_new_object(); | |
748 | snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); | |
749 | json_object_string_add(json_nexthop, "nexthop", buf_nh); | |
750 | if (nexthop->nh_label) | |
751 | json_object_int_add( | |
752 | json_nexthop, "nhLabel", | |
753 | nexthop->nh_label->label[0]); | |
754 | else | |
755 | json_object_string_add(json_nexthop, "nhLabel", | |
756 | "-"); | |
757 | ||
758 | json_object_array_add(json_nexthops, json_nexthop); | |
6bbdd9e9 | 759 | } |
8b117ff0 MS |
760 | |
761 | done: | |
762 | ||
763 | json_object_object_add(json_pw, "nexthops", json_nexthops); | |
6bbdd9e9 | 764 | json_object_array_add(json_pws, json_pw); |
765 | } | |
766 | ||
767 | static void vty_show_mpls_pseudowire_detail_json(struct vty *vty) | |
768 | { | |
769 | json_object *json = NULL; | |
770 | json_object *json_pws = NULL; | |
771 | struct zebra_vrf *zvrf; | |
772 | struct zebra_pw *pw; | |
773 | ||
774 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
775 | if (!zvrf) | |
776 | return; | |
777 | ||
778 | json = json_object_new_object(); | |
779 | json_pws = json_object_new_array(); | |
780 | RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { | |
781 | vty_show_mpls_pseudowire(pw, json_pws); | |
782 | } | |
783 | json_object_object_add(json, "pw", json_pws); | |
962af8a8 | 784 | vty_json(vty, json); |
6bbdd9e9 | 785 | } |
786 | ||
787 | DEFUN(show_pseudowires_detail, show_pseudowires_detail_cmd, | |
788 | "show mpls pseudowires detail [json]$json", | |
789 | SHOW_STR MPLS_STR | |
790 | "Pseudowires\n" | |
791 | "Detailed output\n" JSON_STR) | |
792 | { | |
793 | bool uj = use_json(argc, argv); | |
794 | ||
795 | if (uj) | |
796 | vty_show_mpls_pseudowire_detail_json(vty); | |
797 | else | |
798 | vty_show_mpls_pseudowire_detail(vty); | |
799 | ||
800 | return CMD_SUCCESS; | |
801 | } | |
802 | ||
2dd0d726 RW |
803 | /* Pseudowire configuration write function. */ |
804 | static int zebra_pw_config(struct vty *vty) | |
805 | { | |
806 | int write = 0; | |
807 | struct zebra_vrf *zvrf; | |
808 | struct zebra_pw *pw; | |
809 | ||
810 | zvrf = vrf_info_lookup(VRF_DEFAULT); | |
811 | if (!zvrf) | |
812 | return 0; | |
813 | ||
a2addae8 | 814 | RB_FOREACH (pw, zebra_static_pw_head, &zvrf->static_pseudowires) { |
69965f53 | 815 | vty_out(vty, "pseudowire %s\n", pw->ifname); |
2dd0d726 RW |
816 | if (pw->local_label != MPLS_NO_LABEL |
817 | && pw->remote_label != MPLS_NO_LABEL) | |
69965f53 DL |
818 | vty_out(vty, " mpls label local %u remote %u\n", |
819 | pw->local_label, pw->remote_label); | |
2dd0d726 RW |
820 | else |
821 | vty_out(vty, | |
3efd0893 | 822 | " ! Incomplete config, specify the static MPLS labels\n"); |
2dd0d726 RW |
823 | |
824 | if (pw->af != AF_UNSPEC) { | |
825 | char buf[INET6_ADDRSTRLEN]; | |
826 | inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf)); | |
69965f53 | 827 | vty_out(vty, " neighbor %s\n", buf); |
2dd0d726 RW |
828 | } else |
829 | vty_out(vty, | |
3efd0893 | 830 | " ! Incomplete config, specify a neighbor address\n"); |
2dd0d726 RW |
831 | |
832 | if (!(pw->flags & F_PSEUDOWIRE_CWORD)) | |
69965f53 | 833 | vty_out(vty, " control-word exclude\n"); |
2dd0d726 | 834 | |
07679ad9 | 835 | vty_out(vty, "exit\n"); |
69965f53 | 836 | vty_out(vty, "!\n"); |
2dd0d726 RW |
837 | write = 1; |
838 | } | |
839 | ||
840 | return write; | |
841 | } | |
842 | ||
612c2c15 | 843 | static int zebra_pw_config(struct vty *vty); |
2dd0d726 | 844 | static struct cmd_node pw_node = { |
f4b8291f | 845 | .name = "pw", |
62b346ee | 846 | .node = PW_NODE, |
24389580 | 847 | .parent_node = CONFIG_NODE, |
62b346ee | 848 | .prompt = "%s(config-pw)# ", |
612c2c15 | 849 | .config_write = zebra_pw_config, |
2dd0d726 RW |
850 | }; |
851 | ||
852 | void zebra_pw_vty_init(void) | |
853 | { | |
612c2c15 | 854 | install_node(&pw_node); |
2dd0d726 RW |
855 | install_default(PW_NODE); |
856 | ||
857 | install_element(CONFIG_NODE, &pseudowire_if_cmd); | |
1e9d1183 | 858 | install_element(CONFIG_NODE, &no_pseudowire_if_cmd); |
2dd0d726 RW |
859 | install_element(PW_NODE, &pseudowire_labels_cmd); |
860 | install_element(PW_NODE, &pseudowire_neighbor_cmd); | |
861 | install_element(PW_NODE, &pseudowire_control_word_cmd); | |
862 | ||
863 | install_element(VIEW_NODE, &show_pseudowires_cmd); | |
6bbdd9e9 | 864 | install_element(VIEW_NODE, &show_pseudowires_detail_cmd); |
2dd0d726 | 865 | } |