]> git.proxmox.com Git - mirror_frr.git/blob - pbrd/pbr_vty.c
Merge pull request #2198 from LabNConsulting/working/master/bgpd-nht-crash
[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 "log.h"
29 #include "json.h"
30 #include "debug.h"
31
32 #include "pbrd/pbr_nht.h"
33 #include "pbrd/pbr_map.h"
34 #include "pbrd/pbr_zebra.h"
35 #include "pbrd/pbr_vty.h"
36 #include "pbrd/pbr_debug.h"
37 #ifndef VTYSH_EXTRACT_PL
38 #include "pbrd/pbr_vty_clippy.c"
39 #endif
40
41 DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)",
42 "Create pbr-map or enter pbr-map command mode\n"
43 "The name of the PBR MAP\n"
44 "Sequence to insert in existing pbr-map entry\n"
45 "Sequence number\n")
46 {
47 const char *pbrm_name = argv[1]->arg;
48 uint32_t seqno = atoi(argv[3]->arg);
49 struct pbr_map_sequence *pbrms;
50
51 pbrms = pbrms_get(pbrm_name, seqno);
52 VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms);
53
54 return CMD_SUCCESS;
55 }
56
57 DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]",
58 NO_STR
59 "Delete pbr-map\n"
60 "The name of the PBR MAP\n"
61 "Sequence to delete from existing pbr-map entry\n"
62 "Sequence number\n")
63 {
64 const char *pbrm_name = argv[2]->arg;
65 uint32_t seqno = 0;
66 struct pbr_map *pbrm = pbrm_find(pbrm_name);
67 struct pbr_map_sequence *pbrms;
68 struct listnode *node, *next_node;
69
70 if (argc > 3)
71 seqno = atoi(argv[4]->arg);
72
73 if (!pbrm) {
74 vty_out(vty, "pbr-map %s not found\n", pbrm_name);
75 return CMD_SUCCESS;
76 }
77
78 for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
79 if (seqno && pbrms->seqno != seqno)
80 continue;
81
82 pbr_map_delete(pbrms);
83 }
84
85 return CMD_SUCCESS;
86 }
87
88 DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
89 "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
90 NO_STR
91 "Match the rest of the command\n"
92 "Choose the src ip or ipv6 prefix to use\n"
93 "v4 Prefix\n"
94 "v6 Prefix\n")
95 {
96 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
97
98 pbrms->family = prefix->family;
99
100 if (!no) {
101 if (prefix_same(pbrms->src, prefix))
102 return CMD_SUCCESS;
103
104 if (!pbrms->src)
105 pbrms->src = prefix_new();
106 prefix_copy(pbrms->src, prefix);
107 } else {
108 prefix_free(pbrms->src);
109 pbrms->src = 0;
110 }
111
112 pbr_map_check(pbrms);
113
114 return CMD_SUCCESS;
115 }
116
117 DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
118 "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
119 NO_STR
120 "Match the rest of the command\n"
121 "Choose the src ip or ipv6 prefix to use\n"
122 "v4 Prefix\n"
123 "v6 Prefix\n")
124 {
125 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
126
127 pbrms->family = prefix->family;
128
129 if (!no) {
130 if (prefix_same(pbrms->dst, prefix))
131 return CMD_SUCCESS;
132
133 if (!pbrms->dst)
134 pbrms->dst = prefix_new();
135 prefix_copy(pbrms->dst, prefix);
136 } else {
137 prefix_free(pbrms->dst);
138 pbrms->dst = NULL;
139 }
140
141 pbr_map_check(pbrms);
142
143 return CMD_SUCCESS;
144 }
145
146 DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
147 "[no] set nexthop-group NAME$name",
148 NO_STR
149 "Set for the PBR-MAP\n"
150 "nexthop-group to use\n"
151 "The name of the nexthop-group\n")
152 {
153 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
154 struct nexthop_group_cmd *nhgc;
155
156 if (pbrms->nhg) {
157 vty_out(vty,
158 "A `set nexthop XX` command already exists, please remove that first\n");
159 return CMD_WARNING_CONFIG_FAILED;
160 }
161
162 nhgc = nhgc_find(name);
163 if (!nhgc) {
164 vty_out(vty, "Specified nexthop-group %s does not exist\n",
165 name);
166 vty_out(vty, "PBR-MAP will not be applied until it is created\n");
167 }
168
169 if (no) {
170 if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
171 pbr_map_delete_nexthop_group(pbrms);
172 else {
173 vty_out(vty,
174 "Nexthop Group specified: %s does not exist to remove",
175 name);
176 return CMD_WARNING_CONFIG_FAILED;
177 }
178 } else {
179 if (pbrms->nhgrp_name) {
180 if (strcmp(name, pbrms->nhgrp_name) != 0) {
181 vty_out(vty,
182 "Please delete current nexthop group before modifying current one");
183 return CMD_WARNING_CONFIG_FAILED;
184 }
185
186 return CMD_SUCCESS;
187 }
188 pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
189 pbr_map_check(pbrms);
190 }
191
192 return CMD_SUCCESS;
193 }
194
195 DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
196 "[no] set nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
197 NO_STR
198 "Set for the PBR-MAP\n"
199 "Specify one of the nexthops in this map\n"
200 "v4 Address\n"
201 "v6 Address\n"
202 "Interface to use\n"
203 "If the nexthop is in a different vrf tell us\n"
204 "The nexthop-vrf Name\n")
205 {
206 struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
207 struct vrf *vrf;
208 struct nexthop nhop;
209 struct nexthop *nh;
210
211 if (pbrms->nhgrp_name) {
212 vty_out(vty,
213 "Please unconfigure the nexthop group before adding an individual nexthop");
214 return CMD_WARNING_CONFIG_FAILED;
215 }
216
217 if (name)
218 vrf = vrf_lookup_by_name(name);
219 else
220 vrf = vrf_lookup_by_id(VRF_DEFAULT);
221
222 if (!vrf) {
223 vty_out(vty, "Specified: %s is non-existent\n", name);
224 return CMD_WARNING_CONFIG_FAILED;
225 }
226
227 memset(&nhop, 0, sizeof(nhop));
228 nhop.vrf_id = vrf->vrf_id;
229
230 /*
231 * Make SA happy. CLIPPY is not going to give us a NULL
232 * addr.
233 */
234 assert(addr);
235 if (addr->sa.sa_family == AF_INET) {
236 nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
237 if (intf) {
238 nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
239 nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
240 if (nhop.ifindex == IFINDEX_INTERNAL) {
241 vty_out(vty,
242 "Specified Intf %s does not exist in vrf: %s\n",
243 intf, vrf->name);
244 return CMD_WARNING_CONFIG_FAILED;
245 }
246 } else
247 nhop.type = NEXTHOP_TYPE_IPV4;
248 } else {
249 memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16);
250 if (intf) {
251 nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
252 nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
253 if (nhop.ifindex == IFINDEX_INTERNAL) {
254 vty_out(vty,
255 "Specified Intf %s does not exist in vrf: %s\n",
256 intf, vrf->name);
257 return CMD_WARNING_CONFIG_FAILED;
258 }
259 } else {
260 if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
261 vty_out(vty,
262 "Specified a v6 LL with no interface, rejecting\n");
263 return CMD_WARNING_CONFIG_FAILED;
264 }
265 nhop.type = NEXTHOP_TYPE_IPV6;
266 }
267 }
268
269 if (pbrms->nhg)
270 nh = nexthop_exists(pbrms->nhg, &nhop);
271 else {
272 char buf[PBR_NHC_NAMELEN];
273
274 if (no) {
275 vty_out(vty, "No nexthops to delete");
276 return CMD_WARNING_CONFIG_FAILED;
277 }
278
279 pbrms->nhg = nexthop_group_new();
280 pbrms->internal_nhg_name =
281 XSTRDUP(MTYPE_TMP,
282 pbr_nht_nexthop_make_name(pbrms->parent->name,
283 PBR_NHC_NAMELEN,
284 pbrms->seqno,
285 buf));
286 nh = NULL;
287 }
288
289 if (no) {
290 if (nh)
291 pbr_nht_delete_individual_nexthop(pbrms);
292 } else if (!nh) {
293
294 if (pbrms->nhg->nexthop) {
295 vty_out(vty,
296 "If you would like more than one nexthop please use nexthop-groups");
297 return CMD_WARNING_CONFIG_FAILED;
298 }
299
300 /* must be adding new nexthop since !no and !nexthop_exists */
301 nh = nexthop_new();
302
303 memcpy(nh, &nhop, sizeof(nhop));
304 nexthop_add(&pbrms->nhg->nexthop, nh);
305
306 pbr_nht_add_individual_nexthop(pbrms);
307 pbr_map_check(pbrms);
308 }
309
310 return CMD_SUCCESS;
311 }
312
313 DEFPY (pbr_policy,
314 pbr_policy_cmd,
315 "[no] pbr-policy NAME$mapname",
316 NO_STR
317 "Policy to use\n"
318 "Name of the pbr-map to apply\n")
319 {
320 VTY_DECLVAR_CONTEXT(interface, ifp);
321 struct pbr_map *pbrm, *old_pbrm;
322 struct pbr_interface *pbr_ifp = ifp->info;
323
324 pbrm = pbrm_find(mapname);
325
326 if (!pbr_ifp) {
327 /* we don't want one and we don't have one, so... */
328 if (no)
329 return CMD_SUCCESS;
330
331 /* Some one could have fat fingered the interface name */
332 pbr_ifp = pbr_if_new(ifp);
333 }
334
335 if (no) {
336 if (strcmp(pbr_ifp->mapname, mapname) == 0) {
337 pbr_ifp->mapname[0] = '\0';
338 if (pbrm)
339 pbr_map_interface_delete(pbrm, ifp);
340 }
341 } else {
342 if (strcmp(pbr_ifp->mapname, "") != 0) {
343 old_pbrm = pbrm_find(pbr_ifp->mapname);
344 if (old_pbrm)
345 pbr_map_interface_delete(old_pbrm, ifp);
346 }
347 snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname),
348 "%s", mapname);
349 if (pbrm)
350 pbr_map_add_interface(pbrm, ifp);
351 }
352
353 return CMD_SUCCESS;
354 }
355
356 DEFPY (show_pbr,
357 show_pbr_cmd,
358 "show pbr [json$json]",
359 SHOW_STR
360 "Policy Based Routing\n"
361 JSON_STR)
362 {
363 pbr_nht_write_table_range(vty);
364 pbr_nht_write_rule_range(vty);
365
366 return CMD_SUCCESS;
367 }
368
369 DEFPY (show_pbr_map,
370 show_pbr_map_cmd,
371 "show pbr map [NAME$name] [detail$detail] [json$json]",
372 SHOW_STR
373 "Policy Based Routing\n"
374 "PBR Map\n"
375 "PBR Map Name\n"
376 "Detailed information\n"
377 JSON_STR)
378 {
379 struct pbr_map_sequence *pbrms;
380 struct pbr_map *pbrm;
381 struct listnode *node;
382 char buf[PREFIX_STRLEN];
383 char rbuf[64];
384
385 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
386 if (name && strcmp(name, pbrm->name) != 0)
387 continue;
388
389 vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name,
390 pbrm->valid);
391
392 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
393 if (pbrms->reason)
394 pbr_map_reason_string(pbrms->reason, rbuf,
395 sizeof(rbuf));
396 vty_out(vty,
397 " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
398 pbrms->seqno, pbrms->ruleno, pbrms->installed,
399 pbrms->unique, pbrms->reason ? rbuf : "Valid");
400
401 if (pbrms->src)
402 vty_out(vty, "\tSRC Match: %s\n",
403 prefix2str(pbrms->src, buf,
404 sizeof(buf)));
405 if (pbrms->dst)
406 vty_out(vty, "\tDST Match: %s\n",
407 prefix2str(pbrms->dst, buf,
408 sizeof(buf)));
409
410 if (pbrms->nhgrp_name) {
411 vty_out(vty,
412 "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
413 pbrms->nhgrp_name,
414 pbr_nht_get_table(pbrms->nhgrp_name),
415 pbrms->nhs_installed,
416 pbr_nht_get_installed(
417 pbrms->nhgrp_name));
418 } else if (pbrms->nhg) {
419 vty_out(vty, " ");
420 nexthop_group_write_nexthop(
421 vty, pbrms->nhg->nexthop);
422 vty_out(vty,
423 "\tInstalled: %u(%d) Tableid: %d\n",
424 pbrms->nhs_installed,
425 pbr_nht_get_installed(
426 pbrms->internal_nhg_name),
427 pbr_nht_get_table(
428 pbrms->internal_nhg_name));
429 } else {
430 vty_out(vty,
431 "\tNexthop-Group: Unknown Installed: 0(0)\n");
432 }
433 }
434 }
435 return CMD_SUCCESS;
436 }
437
438 DEFPY(show_pbr_nexthop_group,
439 show_pbr_nexthop_group_cmd,
440 "show pbr nexthop-groups [WORD$word]",
441 SHOW_STR
442 "Policy Based Routing\n"
443 "Nexthop Groups\n"
444 "Optional Name of the nexthop group\n")
445 {
446 pbr_nht_show_nexthop_group(vty, word);
447
448 return CMD_SUCCESS;
449 }
450
451 DEFPY (show_pbr_interface,
452 show_pbr_interface_cmd,
453 "show pbr interface [NAME$name] [json$json]",
454 SHOW_STR
455 "Policy Based Routing\n"
456 "PBR Interface\n"
457 "PBR Interface Name\n"
458 JSON_STR)
459 {
460 struct interface *ifp;
461 struct vrf *vrf;
462 struct pbr_interface *pbr_ifp;
463
464 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
465 FOR_ALL_INTERFACES(vrf, ifp) {
466 struct pbr_map *pbrm;
467
468 if (!ifp->info)
469 continue;
470
471 if (name && strcmp(ifp->name, name) != 0)
472 continue;
473
474 pbr_ifp = ifp->info;
475
476 if (strcmp(pbr_ifp->mapname, "") == 0)
477 continue;
478
479 pbrm = pbrm_find(pbr_ifp->mapname);
480 vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
481 ifp->ifindex, pbr_ifp->mapname);
482 if (!pbrm)
483 vty_out(vty, " (map doesn't exist)");
484 vty_out(vty, "\n");
485 }
486 }
487
488 return CMD_SUCCESS;
489 }
490
491 /* PBR debugging CLI ------------------------------------------------------- */
492 /* clang-format off */
493
494 static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
495
496 DEFPY(debug_pbr,
497 debug_pbr_cmd,
498 "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
499 NO_STR
500 DEBUG_STR
501 "Policy Based Routing\n"
502 "Policy maps\n"
503 "PBRD <-> Zebra communications\n"
504 "Nexthop tracking\n"
505 "Events\n")
506 {
507 uint32_t mode = DEBUG_NODE2MODE(vty->node);
508
509 if (map)
510 DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
511 if (zebra)
512 DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
513 if (nht)
514 DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
515 if (events)
516 DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
517
518 /* no specific debug --> act on all of them */
519 if (strmatch(argv[argc - 1]->text, "pbr"))
520 pbr_debug_set_all(mode, !no);
521
522 return CMD_SUCCESS;
523 }
524
525 DEFUN_NOSH(show_debugging_pbr,
526 show_debugging_pbr_cmd,
527 "show debugging [pbr]",
528 SHOW_STR
529 DEBUG_STR
530 "Policy Based Routing\n")
531 {
532 vty_out(vty, "PBR debugging status:\n");
533
534 pbr_debug_config_write_helper(vty, false);
535
536 return CMD_SUCCESS;
537 }
538
539 /* clang-format on */
540 /* ------------------------------------------------------------------------- */
541
542
543 static struct cmd_node interface_node = {
544 INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
545 };
546
547 static int pbr_interface_config_write(struct vty *vty)
548 {
549 struct interface *ifp;
550 struct vrf *vrf;
551
552 RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
553 FOR_ALL_INTERFACES (vrf, ifp) {
554 if (vrf->vrf_id == VRF_DEFAULT)
555 vty_frame(vty, "interface %s\n", ifp->name);
556 else
557 vty_frame(vty, "interface %s vrf %s\n",
558 ifp->name, vrf->name);
559
560 pbr_map_write_interfaces(vty, ifp);
561
562 vty_endframe(vty, "!\n");
563 }
564 }
565
566 return 1;
567 }
568
569 /* PBR map node structure. */
570 static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1};
571
572 static int pbr_vty_map_config_write_sequence(struct vty *vty,
573 struct pbr_map *pbrm,
574 struct pbr_map_sequence *pbrms)
575 {
576 char buff[PREFIX_STRLEN];
577
578 vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
579
580 if (pbrms->src)
581 vty_out(vty, " match src-ip %s\n",
582 prefix2str(pbrms->src, buff, sizeof(buff)));
583
584 if (pbrms->dst)
585 vty_out(vty, " match dst-ip %s\n",
586 prefix2str(pbrms->dst, buff, sizeof(buff)));
587
588 if (pbrms->nhgrp_name)
589 vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
590
591 if (pbrms->nhg) {
592 vty_out(vty, " set ");
593 nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
594 }
595
596 vty_out(vty, "!\n");
597 return 1;
598 }
599
600 static int pbr_vty_map_config_write(struct vty *vty)
601 {
602 struct pbr_map *pbrm;
603
604 pbr_nht_write_table_range(vty);
605 pbr_nht_write_rule_range(vty);
606
607 RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) {
608 struct pbr_map_sequence *pbrms;
609 struct listnode *node;
610
611 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
612 pbr_vty_map_config_write_sequence(vty, pbrm, pbrms);
613 }
614
615 return 1;
616 }
617
618 void pbr_vty_init(void)
619 {
620 install_node(&interface_node,
621 pbr_interface_config_write);
622 if_cmd_init();
623
624 install_node(&pbr_map_node,
625 pbr_vty_map_config_write);
626
627 /* debug */
628 install_node(&debug_node, pbr_debug_config_write);
629 install_element(VIEW_NODE, &debug_pbr_cmd);
630 install_element(CONFIG_NODE, &debug_pbr_cmd);
631 install_element(VIEW_NODE, &show_debugging_pbr_cmd);
632
633 install_default(PBRMAP_NODE);
634
635 install_element(CONFIG_NODE, &pbr_map_cmd);
636 install_element(CONFIG_NODE, &no_pbr_map_cmd);
637 install_element(INTERFACE_NODE, &pbr_policy_cmd);
638 install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
639 install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
640 install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
641 install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
642 install_element(VIEW_NODE, &show_pbr_cmd);
643 install_element(VIEW_NODE, &show_pbr_map_cmd);
644 install_element(VIEW_NODE, &show_pbr_interface_cmd);
645 install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
646 }