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