]> git.proxmox.com Git - mirror_frr.git/blob - pbrd/pbr_vty.c
Merge pull request #8701 from ton31337/feature/show_ip_bgp_alias
[mirror_frr.git] / pbrd / pbr_vty.c
1 /*
2 * PBR - vty code
3 * Copyright (C) 2018 Cumulus Networks, Inc.
4 * Donald Sharp
5 *
6 * FRR is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * FRR is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <zebra.h>
21
22 #include "vty.h"
23 #include "command.h"
24 #include "prefix.h"
25 #include "vrf.h"
26 #include "nexthop.h"
27 #include "nexthop_group.h"
28 #include "nexthop_group_private.h"
29 #include "log.h"
30 #include "json.h"
31 #include "debug.h"
32 #include "pbr.h"
33
34 #include "pbrd/pbr_nht.h"
35 #include "pbrd/pbr_map.h"
36 #include "pbrd/pbr_zebra.h"
37 #include "pbrd/pbr_vty.h"
38 #include "pbrd/pbr_debug.h"
39 #ifndef VTYSH_EXTRACT_PL
40 #include "pbrd/pbr_vty_clippy.c"
41 #endif
42
43 DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)",
44 "Create pbr-map or enter pbr-map command mode\n"
45 "The name of the PBR MAP\n"
46 "Sequence to insert in existing pbr-map entry\n"
47 "Sequence number\n")
48 {
49 const char *pbrm_name = argv[1]->arg;
50 uint32_t seqno = atoi(argv[3]->arg);
51 struct pbr_map_sequence *pbrms;
52
53 pbrms = pbrms_get(pbrm_name, seqno);
54 VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms);
55
56 return CMD_SUCCESS;
57 }
58
59 DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]",
60 NO_STR
61 "Delete pbr-map\n"
62 "The name of the PBR MAP\n"
63 "Sequence to delete from existing pbr-map entry\n"
64 "Sequence number\n")
65 {
66 const char *pbrm_name = argv[2]->arg;
67 uint32_t seqno = 0;
68 struct pbr_map *pbrm = pbrm_find(pbrm_name);
69 struct pbr_map_sequence *pbrms;
70 struct listnode *node, *next_node;
71
72 if (argc > 3)
73 seqno = atoi(argv[4]->arg);
74
75 if (!pbrm) {
76 vty_out(vty, "pbr-map %s not found\n", pbrm_name);
77 return CMD_SUCCESS;
78 }
79
80 for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
81 if (seqno && pbrms->seqno != seqno)
82 continue;
83
84 pbr_map_delete(pbrms);
85 }
86
87 return CMD_SUCCESS;
88 }
89
90 DEFPY(pbr_set_table_range,
91 pbr_set_table_range_cmd,
92 "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub",
93 PBR_STR
94 "Set table ID range\n"
95 "Set table ID range\n"
96 "Lower bound for table ID range\n"
97 "Upper bound for table ID range\n")
98 {
99 /* upper bound is 2^32 - 2^10 */
100 int ret = CMD_WARNING;
101 const int minrange = 1000;
102
103 /* validate given bounds */
104 if (lb > ub)
105 vty_out(vty, "%% Lower bound must be less than upper bound\n");
106 else if (ub - lb < minrange)
107 vty_out(vty, "%% Range breadth must be at least %d\n", minrange);
108 else {
109 ret = CMD_SUCCESS;
110 pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub);
111 }
112
113 return ret;
114 }
115
116 DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd,
117 "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]",
118 NO_STR
119 PBR_STR
120 "Set table ID range\n"
121 "Set table ID range\n"
122 "Lower bound for table ID range\n"
123 "Upper bound for table ID range\n")
124 {
125 pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID,
126 PBR_NHT_DEFAULT_HIGH_TABLEID);
127 return CMD_SUCCESS;
128 }
129
130 DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
131 "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
132 NO_STR
133 "Match the rest of the command\n"
134 "Choose the src ip or ipv6 prefix to use\n"
135 "v4 Prefix\n"
136 "v6 Prefix\n")
137 {
138 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
139
140 if (pbrms->dst && pbrms->family && prefix->family != pbrms->family) {
141 vty_out(vty, "Cannot mismatch families within match src/dst\n");
142 return CMD_WARNING_CONFIG_FAILED;
143 }
144
145 pbrms->family = prefix->family;
146
147 if (!no) {
148 if (pbrms->src) {
149 if (prefix_same(pbrms->src, prefix))
150 return CMD_SUCCESS;
151 } else
152 pbrms->src = prefix_new();
153
154 prefix_copy(pbrms->src, prefix);
155 } else
156 prefix_free(&pbrms->src);
157
158 pbr_map_check(pbrms, true);
159
160 return CMD_SUCCESS;
161 }
162
163 DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
164 "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
165 NO_STR
166 "Match the rest of the command\n"
167 "Choose the dst ip or ipv6 prefix to use\n"
168 "v4 Prefix\n"
169 "v6 Prefix\n")
170 {
171 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
172
173 if (pbrms->src && pbrms->family && prefix->family != pbrms->family) {
174 vty_out(vty, "Cannot mismatch families within match src/dst\n");
175 return CMD_WARNING_CONFIG_FAILED;
176 }
177
178 pbrms->family = prefix->family;
179
180 if (!no) {
181 if (pbrms->dst) {
182 if (prefix_same(pbrms->dst, prefix))
183 return CMD_SUCCESS;
184 } else
185 pbrms->dst = prefix_new();
186
187 prefix_copy(pbrms->dst, prefix);
188 } else
189 prefix_free(&pbrms->dst);
190
191 pbr_map_check(pbrms, true);
192
193 return CMD_SUCCESS;
194 }
195
196 DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
197 "[no] match dscp DSCP$dscp",
198 NO_STR
199 "Match the rest of the command\n"
200 "Match based on IP DSCP field\n"
201 "DSCP value (below 64) or standard codepoint name\n")
202 {
203 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
204 char dscpname[100];
205 uint8_t rawDscp;
206
207 /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */
208 bool isANumber = true;
209 for (int i = 0; i < (int)strlen(dscp); i++) {
210 /* Letters are not numbers */
211 if (!isdigit(dscp[i]))
212 isANumber = false;
213
214 /* Lowercase the dscp enum (if needed) */
215 if (isupper(dscp[i]))
216 dscpname[i] = tolower(dscp[i]);
217 else
218 dscpname[i] = dscp[i];
219 }
220 dscpname[strlen(dscp)] = '\0';
221
222 if (isANumber) {
223 /* dscp passed is a regular number */
224 long dscpAsNum = strtol(dscp, NULL, 0);
225
226 if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) {
227 /* Refuse to install on overflow */
228 vty_out(vty, "dscp (%s) must be less than 64\n", dscp);
229 return CMD_WARNING_CONFIG_FAILED;
230 }
231 rawDscp = dscpAsNum;
232 } else {
233 /* check dscp if it is an enum like cs0 */
234 rawDscp = pbr_map_decode_dscp_enum(dscpname);
235 if (rawDscp > PBR_DSFIELD_DSCP) {
236 vty_out(vty, "Invalid dscp value: %s\n", dscpname);
237 return CMD_WARNING_CONFIG_FAILED;
238 }
239 }
240
241 if (!no) {
242 if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
243 return CMD_SUCCESS;
244
245 /* Set the DSCP bits of the DSField */
246 pbrms->dsfield =
247 (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
248 } else {
249 pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
250 }
251
252 pbr_map_check(pbrms, true);
253
254 return CMD_SUCCESS;
255 }
256
257 DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
258 "[no] match ecn (0-3)$ecn",
259 NO_STR
260 "Match the rest of the command\n"
261 "Match based on IP ECN field\n"
262 "Explicit Congestion Notification\n")
263 {
264 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
265
266 if (!no) {
267 if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
268 return CMD_SUCCESS;
269
270 /* Set the ECN bits of the DSField */
271 pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
272 } else {
273 pbrms->dsfield &= ~PBR_DSFIELD_ECN;
274 }
275
276 pbr_map_check(pbrms, true);
277
278 return CMD_SUCCESS;
279 }
280
281 DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
282 "[no] match mark (1-4294967295)$mark",
283 NO_STR
284 "Match the rest of the command\n"
285 "Choose the mark value to use\n"
286 "mark\n")
287 {
288 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
289
290 #ifndef GNU_LINUX
291 vty_out(vty, "pbr marks are not supported on this platform");
292 return CMD_WARNING_CONFIG_FAILED;
293 #endif
294
295 if (!no) {
296 if (pbrms->mark)
297 if (pbrms->mark == (uint32_t)mark)
298 return CMD_SUCCESS;
299
300 pbrms->mark = (uint32_t)mark;
301 } else
302 pbrms->mark = 0;
303
304 pbr_map_check(pbrms, true);
305
306 return CMD_SUCCESS;
307 }
308
309 static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms)
310 {
311 if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
312 pbr_map_delete_vrf(pbrms);
313 pbrms->vrf_name[0] = '\0';
314 pbrms->vrf_lookup = false;
315 pbrms->vrf_unchanged = false;
316 }
317 }
318
319 static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms)
320 {
321 if (pbrms->nhgrp_name)
322 pbr_map_delete_nexthops(pbrms);
323 }
324
325 static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms)
326 {
327 if (pbrms->nhg)
328 pbr_nht_delete_individual_nexthop(pbrms);
329 }
330
331 static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms)
332 {
333 pbrms_clear_set_vrf_config(pbrms);
334 pbrms_clear_set_nhg_config(pbrms);
335 pbrms_clear_set_nexthop_config(pbrms);
336
337 pbrms->nhs_installed = false;
338 }
339
340 DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
341 "set nexthop-group NHGNAME$name",
342 "Set for the PBR-MAP\n"
343 "nexthop-group to use\n"
344 "The name of the nexthop-group\n")
345 {
346 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
347 struct nexthop_group_cmd *nhgc;
348
349 nhgc = nhgc_find(name);
350 if (!nhgc) {
351 vty_out(vty, "Specified nexthop-group %s does not exist\n",
352 name);
353 vty_out(vty,
354 "PBR-MAP will not be applied until it is created\n");
355 }
356
357 if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
358 return CMD_SUCCESS;
359
360 /* This is new/replacement config */
361 pbrms_clear_set_config(pbrms);
362
363 pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
364 pbr_map_check(pbrms, true);
365
366 return CMD_SUCCESS;
367 }
368
369 DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd,
370 "no set nexthop-group [NHGNAME$name]",
371 NO_STR
372 "Set for the PBR-MAP\n"
373 "nexthop-group to use\n"
374 "The name of the nexthop-group\n")
375 {
376 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
377
378 pbrms_clear_set_config(pbrms);
379
380 return CMD_SUCCESS;
381 }
382
383 DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
384 "set nexthop\
385 <\
386 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
387 |INTERFACE$intf\
388 >\
389 [nexthop-vrf NAME$vrf_name]",
390 "Set for the PBR-MAP\n"
391 "Specify one of the nexthops in this map\n"
392 "v4 Address\n"
393 "v6 Address\n"
394 "Interface to use\n"
395 "Interface to use\n"
396 "If the nexthop is in a different vrf tell us\n"
397 "The nexthop-vrf Name\n")
398 {
399 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
400 struct vrf *vrf;
401 struct nexthop nhop;
402 struct nexthop *nh = NULL;
403
404 if (vrf_name)
405 vrf = vrf_lookup_by_name(vrf_name);
406 else
407 vrf = vrf_lookup_by_id(VRF_DEFAULT);
408
409 if (!vrf) {
410 vty_out(vty, "Specified VRF: %s is non-existent\n", vrf_name);
411 return CMD_WARNING_CONFIG_FAILED;
412 }
413
414 memset(&nhop, 0, sizeof(nhop));
415 nhop.vrf_id = vrf->vrf_id;
416
417 if (intf) {
418 struct interface *ifp;
419
420 ifp = if_lookup_by_name_all_vrf(intf);
421 if (!ifp) {
422 vty_out(vty, "Specified Intf %s does not exist\n",
423 intf);
424 return CMD_WARNING_CONFIG_FAILED;
425 }
426 if (ifp->vrf_id != vrf->vrf_id) {
427 struct vrf *actual;
428
429 actual = vrf_lookup_by_id(ifp->vrf_id);
430 vty_out(vty,
431 "Specified Intf %s is not in vrf %s but is in vrf %s, using actual vrf\n",
432 ifp->name, vrf->name, actual->name);
433 }
434 nhop.ifindex = ifp->ifindex;
435 nhop.vrf_id = ifp->vrf_id;
436 }
437
438 if (addr) {
439 if (addr->sa.sa_family == AF_INET) {
440 nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
441 if (intf)
442 nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
443 else
444 nhop.type = NEXTHOP_TYPE_IPV4;
445 } else {
446 nhop.gate.ipv6 = addr->sin6.sin6_addr;
447 if (intf)
448 nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
449 else {
450 if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
451 vty_out(vty,
452 "Specified a v6 LL with no interface, rejecting\n");
453 return CMD_WARNING_CONFIG_FAILED;
454 }
455 nhop.type = NEXTHOP_TYPE_IPV6;
456 }
457 }
458 } else
459 nhop.type = NEXTHOP_TYPE_IFINDEX;
460
461 if (pbrms->nhg)
462 nh = nexthop_exists(pbrms->nhg, &nhop);
463
464 if (nh) /* Same config re-entered */
465 goto done;
466
467 /* This is new/replacement config */
468 pbrms_clear_set_config(pbrms);
469
470 pbr_nht_add_individual_nexthop(pbrms, &nhop);
471
472 pbr_map_check(pbrms, true);
473
474 done:
475 if (nhop.type == NEXTHOP_TYPE_IFINDEX
476 || (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX
477 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) {
478 struct interface *ifp;
479
480 ifp = if_lookup_by_index(nhop.ifindex, nhop.vrf_id);
481 if (ifp)
482 pbr_nht_nexthop_interface_update(ifp);
483 }
484
485 return CMD_SUCCESS;
486 }
487
488 DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd,
489 "no set nexthop\
490 [<\
491 <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
492 |INTERFACE$intf\
493 >\
494 [nexthop-vrf NAME$vrf_name]]",
495 NO_STR
496 "Set for the PBR-MAP\n"
497 "Specify one of the nexthops in this map\n"
498 "v4 Address\n"
499 "v6 Address\n"
500 "Interface to use\n"
501 "Interface to use\n"
502 "If the nexthop is in a different vrf tell us\n"
503 "The nexthop-vrf Name\n")
504 {
505 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
506
507 pbrms_clear_set_config(pbrms);
508
509 return CMD_SUCCESS;
510 }
511
512 DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
513 "set vrf <NAME$vrf_name|unchanged>",
514 "Set for the PBR-MAP\n"
515 "Specify the VRF for this map\n"
516 "The VRF Name\n"
517 "Use the interface's VRF for lookup\n")
518 {
519 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
520
521 /*
522 * If an equivalent set vrf * exists, just return success.
523 */
524 if (vrf_name && pbrms->vrf_lookup
525 && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0)
526 return CMD_SUCCESS;
527 else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */
528 return CMD_SUCCESS;
529
530 if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) {
531 vty_out(vty, "Specified: %s is non-existent\n", vrf_name);
532 return CMD_WARNING_CONFIG_FAILED;
533 }
534
535 /* This is new/replacement config */
536 pbrms_clear_set_config(pbrms);
537
538 if (vrf_name) {
539 pbrms->vrf_lookup = true;
540 strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name));
541 } else
542 pbrms->vrf_unchanged = true;
543
544 pbr_map_check(pbrms, true);
545
546 return CMD_SUCCESS;
547 }
548
549 DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd,
550 "no set vrf [<NAME$vrf_name|unchanged>]",
551 NO_STR
552 "Set for the PBR-MAP\n"
553 "Specify the VRF for this map\n"
554 "The VRF Name\n"
555 "Use the interface's VRF for lookup\n")
556 {
557 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
558
559 pbrms_clear_set_config(pbrms);
560
561 return CMD_SUCCESS;
562 }
563
564 DEFPY (pbr_policy,
565 pbr_policy_cmd,
566 "[no] pbr-policy PBRMAP$mapname",
567 NO_STR
568 "Policy to use\n"
569 "Name of the pbr-map to apply\n")
570 {
571 VTY_DECLVAR_CONTEXT(interface, ifp);
572 struct pbr_map *pbrm, *old_pbrm;
573 struct pbr_interface *pbr_ifp = ifp->info;
574
575 old_pbrm = NULL;
576 pbrm = pbrm_find(mapname);
577
578 if (!pbr_ifp) {
579 /* we don't want one and we don't have one, so... */
580 if (no)
581 return CMD_SUCCESS;
582
583 /* Some one could have fat fingered the interface name */
584 pbr_ifp = pbr_if_new(ifp);
585 }
586
587 if (no) {
588 if (strcmp(pbr_ifp->mapname, mapname) == 0) {
589 pbr_ifp->mapname[0] = '\0';
590 if (pbrm)
591 pbr_map_interface_delete(pbrm, ifp);
592 }
593 } else {
594 if (strcmp(pbr_ifp->mapname, "") != 0) {
595 old_pbrm = pbrm_find(pbr_ifp->mapname);
596
597 /*
598 * So if we have an old pbrm we should only
599 * delete it if we are actually deleting and
600 * moving to a new pbrm
601 */
602 if (old_pbrm && old_pbrm != pbrm)
603 pbr_map_interface_delete(old_pbrm, ifp);
604 }
605 snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname),
606 "%s", mapname);
607
608 /*
609 * So only reinstall if the old_pbrm and this pbrm are
610 * different.
611 */
612 if (pbrm && pbrm != old_pbrm)
613 pbr_map_add_interface(pbrm, ifp);
614 }
615
616 return CMD_SUCCESS;
617 }
618
619 DEFPY (show_pbr,
620 show_pbr_cmd,
621 "show pbr",
622 SHOW_STR
623 PBR_STR)
624 {
625 pbr_nht_write_table_range(vty);
626 pbr_nht_write_rule_range(vty);
627
628 return CMD_SUCCESS;
629 }
630
631 static void
632 pbrms_nexthop_group_write_individual_nexthop(
633 struct vty *vty, const struct pbr_map_sequence *pbrms)
634 {
635 struct pbr_nexthop_group_cache find;
636 struct pbr_nexthop_group_cache *pnhgc;
637 struct pbr_nexthop_cache lookup;
638 struct pbr_nexthop_cache *pnhc;
639
640 memset(&find, 0, sizeof(find));
641 strlcpy(find.name, pbrms->internal_nhg_name, sizeof(find.name));
642
643 pnhgc = hash_lookup(pbr_nhg_hash, &find);
644 assert(pnhgc);
645
646 lookup.nexthop = *pbrms->nhg->nexthop;
647 pnhc = hash_lookup(pnhgc->nhh, &lookup);
648
649 nexthop_group_write_nexthop_simple(
650 vty, pbrms->nhg->nexthop,
651 pnhc->nexthop.ifindex != 0 ? pnhc->intf_name : NULL);
652 if (pnhc->nexthop.vrf_id != VRF_DEFAULT)
653 vty_out(vty, " nexthop-vrf %s", pnhc->vrf_name);
654
655 vty_out(vty, "\n");
656 }
657
658 static void vty_show_pbrms(struct vty *vty,
659 const struct pbr_map_sequence *pbrms, bool detail)
660 {
661 char rbuf[64];
662
663 if (pbrms->reason)
664 pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
665
666 vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno);
667
668 if (detail)
669 vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n",
670 pbrms->installed, pbrms->unique,
671 pbrms->reason ? rbuf : "Valid");
672 else
673 vty_out(vty, " Installed: %s Reason: %s\n",
674 pbrms->installed ? "yes" : "no",
675 pbrms->reason ? rbuf : "Valid");
676
677 if (pbrms->src)
678 vty_out(vty, " SRC Match: %pFX\n", pbrms->src);
679 if (pbrms->dst)
680 vty_out(vty, " DST Match: %pFX\n", pbrms->dst);
681 if (pbrms->dsfield & PBR_DSFIELD_DSCP)
682 vty_out(vty, " DSCP Match: %u\n",
683 (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
684 if (pbrms->dsfield & PBR_DSFIELD_ECN)
685 vty_out(vty, " ECN Match: %u\n",
686 pbrms->dsfield & PBR_DSFIELD_ECN);
687 if (pbrms->mark)
688 vty_out(vty, " MARK Match: %u\n", pbrms->mark);
689
690 if (pbrms->nhgrp_name) {
691 vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name);
692
693 if (detail)
694 vty_out(vty,
695 " Installed: %u(%d) Tableid: %d\n",
696 pbrms->nhs_installed,
697 pbr_nht_get_installed(pbrms->nhgrp_name),
698 pbr_nht_get_table(pbrms->nhgrp_name));
699 else
700 vty_out(vty, " Installed: %s Tableid: %d\n",
701 pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
702 : "no",
703 pbr_nht_get_table(pbrms->nhgrp_name));
704
705 } else if (pbrms->nhg) {
706 vty_out(vty, " ");
707 pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
708 if (detail)
709 vty_out(vty,
710 " Installed: %u(%d) Tableid: %d\n",
711 pbrms->nhs_installed,
712 pbr_nht_get_installed(pbrms->internal_nhg_name),
713 pbr_nht_get_table(pbrms->internal_nhg_name));
714 else
715 vty_out(vty, " Installed: %s Tableid: %d\n",
716 pbr_nht_get_installed(pbrms->internal_nhg_name)
717 ? "yes"
718 : "no",
719 pbr_nht_get_table(pbrms->internal_nhg_name));
720
721 } else if (pbrms->vrf_unchanged) {
722 vty_out(vty, " VRF Unchanged (use interface vrf)\n");
723 } else if (pbrms->vrf_lookup) {
724 vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name);
725 } else {
726 vty_out(vty, " Nexthop-Group: Unknown Installed: no\n");
727 }
728 }
729
730 static void vty_json_pbrms(json_object *j, struct vty *vty,
731 const struct pbr_map_sequence *pbrms)
732 {
733 json_object *jpbrm, *nexthop_group;
734 char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name
735 : pbrms->internal_nhg_name;
736 char buf[PREFIX_STRLEN];
737 char rbuf[64];
738
739 jpbrm = json_object_new_object();
740
741 json_object_int_add(jpbrm, "id", pbrms->unique);
742
743 if (pbrms->reason)
744 pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
745
746 json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno);
747 json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno);
748 json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged);
749 json_object_boolean_add(jpbrm, "installed",
750 pbr_nht_get_installed(nhg_name));
751 json_object_string_add(jpbrm, "installedReason",
752 pbrms->reason ? rbuf : "Valid");
753
754 if (nhg_name) {
755 nexthop_group = json_object_new_object();
756
757 json_object_int_add(nexthop_group, "tableId",
758 pbr_nht_get_table(nhg_name));
759 json_object_string_add(nexthop_group, "name", nhg_name);
760 json_object_boolean_add(nexthop_group, "installed",
761 pbr_nht_get_installed(nhg_name));
762 json_object_int_add(nexthop_group, "installedInternally",
763 pbrms->nhs_installed);
764
765 json_object_object_add(jpbrm, "nexthopGroup", nexthop_group);
766 }
767
768 if (pbrms->vrf_lookup)
769 json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name);
770
771 if (pbrms->src)
772 json_object_string_add(
773 jpbrm, "matchSrc",
774 prefix2str(pbrms->src, buf, sizeof(buf)));
775 if (pbrms->dst)
776 json_object_string_add(
777 jpbrm, "matchDst",
778 prefix2str(pbrms->dst, buf, sizeof(buf)));
779 if (pbrms->mark)
780 json_object_int_add(jpbrm, "matchMark", pbrms->mark);
781 if (pbrms->dsfield & PBR_DSFIELD_DSCP)
782 json_object_int_add(jpbrm, "matchDscp",
783 (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
784 if (pbrms->dsfield & PBR_DSFIELD_ECN)
785 json_object_int_add(jpbrm, "matchEcn",
786 pbrms->dsfield & PBR_DSFIELD_ECN);
787
788 json_object_array_add(j, jpbrm);
789 }
790
791 static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
792 bool detail)
793 {
794 struct pbr_map_sequence *pbrms;
795 struct listnode *node;
796
797 vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name,
798 pbrm->valid ? "yes" : "no");
799
800 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
801 vty_show_pbrms(vty, pbrms, detail);
802 }
803
804 static void vty_json_pbr_map(json_object *j, struct vty *vty,
805 const struct pbr_map *pbrm)
806 {
807 struct pbr_map_sequence *pbrms;
808 struct listnode *node;
809 json_object *jpbrms;
810
811 json_object_string_add(j, "name", pbrm->name);
812 json_object_boolean_add(j, "valid", pbrm->valid);
813
814 jpbrms = json_object_new_array();
815
816 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
817 vty_json_pbrms(jpbrms, vty, pbrms);
818
819 json_object_object_add(j, "policies", jpbrms);
820 }
821
822 DEFPY (show_pbr_map,
823 show_pbr_map_cmd,
824 "show pbr map [NAME$name] [detail$detail|json$json]",
825 SHOW_STR
826 PBR_STR
827 "PBR Map\n"
828 "PBR Map Name\n"
829 "Detailed information\n"
830 JSON_STR)
831 {
832 struct pbr_map *pbrm;
833 json_object *j = NULL;
834
835 if (json)
836 j = json_object_new_array();
837
838 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
839 json_object *this_map = NULL;
840 if (name && strcmp(name, pbrm->name) != 0)
841 continue;
842
843 if (j)
844 this_map = json_object_new_object();
845
846 if (this_map) {
847 vty_json_pbr_map(this_map, vty, pbrm);
848
849 json_object_array_add(j, this_map);
850 continue;
851 }
852
853 vty_show_pbr_map(vty, pbrm, detail);
854 }
855
856 if (j) {
857 vty_out(vty, "%s\n",
858 json_object_to_json_string_ext(
859 j, JSON_C_TO_STRING_PRETTY));
860 json_object_free(j);
861 }
862
863 return CMD_SUCCESS;
864 }
865
866 DEFPY(show_pbr_nexthop_group,
867 show_pbr_nexthop_group_cmd,
868 "show pbr nexthop-groups [WORD$word] [json$json]",
869 SHOW_STR
870 PBR_STR
871 "Nexthop Groups\n"
872 "Optional Name of the nexthop group\n"
873 JSON_STR)
874 {
875 json_object *j = NULL;
876
877 if (json)
878 j = json_object_new_array();
879
880 if (j) {
881 pbr_nht_json_nexthop_group(j, word);
882
883 vty_out(vty, "%s\n",
884 json_object_to_json_string_ext(
885 j, JSON_C_TO_STRING_PRETTY));
886
887 json_object_free(j);
888 } else
889 pbr_nht_show_nexthop_group(vty, word);
890
891
892 return CMD_SUCCESS;
893 }
894
895 DEFPY (show_pbr_interface,
896 show_pbr_interface_cmd,
897 "show pbr interface [NAME$name] [json$json]",
898 SHOW_STR
899 PBR_STR
900 "PBR Interface\n"
901 "PBR Interface Name\n"
902 JSON_STR)
903 {
904 struct interface *ifp;
905 struct vrf *vrf;
906 struct pbr_interface *pbr_ifp;
907 json_object *j = NULL;
908
909 if (json)
910 j = json_object_new_array();
911
912 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
913 FOR_ALL_INTERFACES(vrf, ifp) {
914 struct pbr_map *pbrm;
915 json_object *this_iface = NULL;
916
917 if (j)
918 this_iface = json_object_new_object();
919
920 if (!ifp->info) {
921 json_object_free(this_iface);
922 continue;
923 }
924
925 if (name && strcmp(ifp->name, name) != 0) {
926 json_object_free(this_iface);
927 continue;
928 }
929
930 pbr_ifp = ifp->info;
931
932 if (strcmp(pbr_ifp->mapname, "") == 0) {
933 json_object_free(this_iface);
934 continue;
935 }
936
937 pbrm = pbrm_find(pbr_ifp->mapname);
938
939 if (this_iface) {
940 json_object_string_add(this_iface, "name",
941 ifp->name);
942 json_object_int_add(this_iface, "index",
943 ifp->ifindex);
944 json_object_string_add(this_iface, "policy",
945 pbr_ifp->mapname);
946 json_object_boolean_add(this_iface, "valid",
947 pbrm);
948
949 json_object_array_add(j, this_iface);
950 continue;
951 }
952
953 vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
954 ifp->ifindex, pbr_ifp->mapname);
955 if (!pbrm)
956 vty_out(vty, " (map doesn't exist)");
957 vty_out(vty, "\n");
958 }
959 }
960
961 if (j) {
962 vty_out(vty, "%s\n",
963 json_object_to_json_string_ext(
964 j, JSON_C_TO_STRING_PRETTY));
965 json_object_free(j);
966 }
967
968 return CMD_SUCCESS;
969 }
970
971 /* PBR debugging CLI ------------------------------------------------------- */
972
973 static struct cmd_node debug_node = {
974 .name = "debug",
975 .node = DEBUG_NODE,
976 .prompt = "",
977 .config_write = pbr_debug_config_write,
978 };
979
980 DEFPY(debug_pbr,
981 debug_pbr_cmd,
982 "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
983 NO_STR
984 DEBUG_STR
985 PBR_STR
986 "Policy maps\n"
987 "PBRD <-> Zebra communications\n"
988 "Nexthop tracking\n"
989 "Events\n")
990 {
991 uint32_t mode = DEBUG_NODE2MODE(vty->node);
992
993 if (map)
994 DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
995 if (zebra)
996 DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
997 if (nht)
998 DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
999 if (events)
1000 DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
1001
1002 /* no specific debug --> act on all of them */
1003 if (strmatch(argv[argc - 1]->text, "pbr"))
1004 pbr_debug_set_all(mode, !no);
1005
1006 return CMD_SUCCESS;
1007 }
1008
1009 DEFUN_NOSH(show_debugging_pbr,
1010 show_debugging_pbr_cmd,
1011 "show debugging [pbr]",
1012 SHOW_STR
1013 DEBUG_STR
1014 PBR_STR)
1015 {
1016 vty_out(vty, "PBR debugging status:\n");
1017
1018 pbr_debug_config_write_helper(vty, false);
1019
1020 return CMD_SUCCESS;
1021 }
1022
1023 /* ------------------------------------------------------------------------- */
1024
1025
1026 static int pbr_interface_config_write(struct vty *vty);
1027 static struct cmd_node interface_node = {
1028 .name = "interface",
1029 .node = INTERFACE_NODE,
1030 .parent_node = CONFIG_NODE,
1031 .prompt = "%s(config-if)# ",
1032 .config_write = pbr_interface_config_write,
1033 };
1034
1035 static int pbr_interface_config_write(struct vty *vty)
1036 {
1037 struct interface *ifp;
1038 struct vrf *vrf;
1039
1040 RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
1041 FOR_ALL_INTERFACES (vrf, ifp) {
1042 if (vrf->vrf_id == VRF_DEFAULT)
1043 vty_frame(vty, "interface %s\n", ifp->name);
1044 else
1045 vty_frame(vty, "interface %s vrf %s\n",
1046 ifp->name, vrf->name);
1047
1048 if (ifp->desc)
1049 vty_out(vty, " description %s\n", ifp->desc);
1050
1051 pbr_map_write_interfaces(vty, ifp);
1052
1053 vty_endframe(vty, "!\n");
1054 }
1055 }
1056
1057 return 1;
1058 }
1059
1060 static int pbr_vty_map_config_write(struct vty *vty);
1061 /* PBR map node structure. */
1062 static struct cmd_node pbr_map_node = {
1063 .name = "pbr-map",
1064 .node = PBRMAP_NODE,
1065 .parent_node = CONFIG_NODE,
1066 .prompt = "%s(config-pbr-map)# ",
1067 .config_write = pbr_vty_map_config_write,
1068 };
1069
1070 static int pbr_vty_map_config_write_sequence(struct vty *vty,
1071 struct pbr_map *pbrm,
1072 struct pbr_map_sequence *pbrms)
1073 {
1074 vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
1075
1076 if (pbrms->src)
1077 vty_out(vty, " match src-ip %pFX\n", pbrms->src);
1078
1079 if (pbrms->dst)
1080 vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
1081
1082 if (pbrms->dsfield & PBR_DSFIELD_DSCP)
1083 vty_out(vty, " match dscp %u\n",
1084 (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
1085
1086 if (pbrms->dsfield & PBR_DSFIELD_ECN)
1087 vty_out(vty, " match ecn %u\n",
1088 pbrms->dsfield & PBR_DSFIELD_ECN);
1089
1090 if (pbrms->mark)
1091 vty_out(vty, " match mark %u\n", pbrms->mark);
1092
1093 if (pbrms->vrf_unchanged)
1094 vty_out(vty, " set vrf unchanged\n");
1095
1096 if (pbrms->vrf_lookup)
1097 vty_out(vty, " set vrf %s\n", pbrms->vrf_name);
1098
1099 if (pbrms->nhgrp_name)
1100 vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
1101
1102 if (pbrms->nhg) {
1103 vty_out(vty, " set ");
1104 pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
1105 }
1106
1107 vty_out(vty, "!\n");
1108 return 1;
1109 }
1110
1111 static int pbr_vty_map_config_write(struct vty *vty)
1112 {
1113 struct pbr_map *pbrm;
1114
1115 pbr_nht_write_table_range(vty);
1116 pbr_nht_write_rule_range(vty);
1117
1118 RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) {
1119 struct pbr_map_sequence *pbrms;
1120 struct listnode *node;
1121
1122 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
1123 pbr_vty_map_config_write_sequence(vty, pbrm, pbrms);
1124 }
1125
1126 return 1;
1127 }
1128
1129 static void pbr_map_completer(vector comps, struct cmd_token *token)
1130 {
1131 struct pbr_map *pbrm;
1132
1133 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps)
1134 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, pbrm->name));
1135 }
1136
1137 static const struct cmd_variable_handler pbr_map_name[] = {
1138 {
1139 .tokenname = "PBRMAP", .completions = pbr_map_completer,
1140 },
1141 {
1142 .completions = NULL
1143 }
1144 };
1145
1146 void pbr_vty_init(void)
1147 {
1148 cmd_variable_handler_register(pbr_map_name);
1149
1150 install_node(&interface_node);
1151 if_cmd_init();
1152
1153 install_node(&pbr_map_node);
1154
1155 /* debug */
1156 install_node(&debug_node);
1157 install_element(ENABLE_NODE, &debug_pbr_cmd);
1158 install_element(CONFIG_NODE, &debug_pbr_cmd);
1159 install_element(ENABLE_NODE, &show_debugging_pbr_cmd);
1160
1161 install_default(PBRMAP_NODE);
1162
1163 install_element(CONFIG_NODE, &pbr_map_cmd);
1164 install_element(CONFIG_NODE, &no_pbr_map_cmd);
1165 install_element(CONFIG_NODE, &pbr_set_table_range_cmd);
1166 install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd);
1167 install_element(INTERFACE_NODE, &pbr_policy_cmd);
1168 install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
1169 install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
1170 install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
1171 install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
1172 install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
1173 install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
1174 install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
1175 install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
1176 install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd);
1177 install_element(PBRMAP_NODE, &pbr_map_vrf_cmd);
1178 install_element(PBRMAP_NODE, &no_pbr_map_vrf_cmd);
1179 install_element(VIEW_NODE, &show_pbr_cmd);
1180 install_element(VIEW_NODE, &show_pbr_map_cmd);
1181 install_element(VIEW_NODE, &show_pbr_interface_cmd);
1182 install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
1183 }