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