]> git.proxmox.com Git - mirror_frr.git/blame - pbrd/pbr_vty.c
pbrd, zebra: Properly notice rule deletion
[mirror_frr.git] / pbrd / pbr_vty.c
CommitLineData
e5c83d9b
DS
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"
e5c83d9b
DS
36#include "pbrd/pbr_debug.h"
37#ifndef VTYSH_EXTRACT_PL
38#include "pbrd/pbr_vty_clippy.c"
39#endif
40
41DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)",
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
57DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
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);
e5c83d9b
DS
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
b13e5ad6
DS
78 for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
79 if (seqno && pbrms->seqno != seqno)
80 continue;
e5c83d9b 81
b13e5ad6
DS
82 pbr_map_delete(pbrms);
83 }
e5c83d9b
DS
84
85 return CMD_SUCCESS;
86}
87
88DEFPY(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);
e5c83d9b 97
49027ce8
DS
98 pbrms->family = prefix->family;
99
e5c83d9b 100 if (!no) {
b8eb036f
DS
101 if (prefix_same(pbrms->src, prefix))
102 return CMD_SUCCESS;
103
e5c83d9b
DS
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
b13e5ad6 112 pbr_map_check(pbrms);
e5c83d9b
DS
113
114 return CMD_SUCCESS;
115}
116
117DEFPY(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);
e5c83d9b 126
49027ce8
DS
127 pbrms->family = prefix->family;
128
e5c83d9b 129 if (!no) {
b8eb036f
DS
130 if (prefix_same(pbrms->dst, prefix))
131 return CMD_SUCCESS;
132
e5c83d9b
DS
133 if (!pbrms->dst)
134 pbrms->dst = prefix_new();
135 prefix_copy(pbrms->dst, prefix);
136 } else {
137 prefix_free(pbrms->dst);
b13e5ad6 138 pbrms->dst = NULL;
e5c83d9b
DS
139 }
140
b13e5ad6 141 pbr_map_check(pbrms);
e5c83d9b
DS
142
143 return CMD_SUCCESS;
144}
145
146DEFPY(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;
e5c83d9b 155
e042a421
DS
156 if (pbrms->nhg) {
157 vty_out(vty,
158 "A `set nexthop XX` command already exists, please remove that first\n");
3a9210c2 159 return CMD_WARNING_CONFIG_FAILED;
e042a421
DS
160 }
161
e5c83d9b
DS
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)
b13e5ad6 171 pbr_map_delete_nexthop_group(pbrms);
e5c83d9b
DS
172 else {
173 vty_out(vty,
174 "Nexthop Group specified: %s does not exist to remove",
175 name);
3a9210c2 176 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
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");
3a9210c2 183 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
184 }
185
186 return CMD_SUCCESS;
187 }
188 pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
b13e5ad6 189 pbr_map_check(pbrms);
e5c83d9b
DS
190 }
191
e5c83d9b
DS
192 return CMD_SUCCESS;
193}
194
195DEFPY(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;
e5c83d9b
DS
210
211 if (pbrms->nhgrp_name) {
212 vty_out(vty,
213 "Please unconfigure the nexthop group before adding an individual nexthop");
3a9210c2 214 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
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);
3a9210c2 224 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
225 }
226
227 memset(&nhop, 0, sizeof(nhop));
228 nhop.vrf_id = vrf->vrf_id;
229
230 if (addr->sa.sa_family == AF_INET) {
231 nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
232 if (intf) {
233 nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
234 nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
235 if (nhop.ifindex == IFINDEX_INTERNAL) {
236 vty_out(vty,
237 "Specified Intf %s does not exist in vrf: %s\n",
238 intf, vrf->name);
3a9210c2 239 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
240 }
241 } else
242 nhop.type = NEXTHOP_TYPE_IPV4;
243 } else {
244 memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16);
245 if (intf) {
246 nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
247 nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
248 if (nhop.ifindex == IFINDEX_INTERNAL) {
249 vty_out(vty,
250 "Specified Intf %s does not exist in vrf: %s\n",
251 intf, vrf->name);
3a9210c2 252 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b 253 }
cafec8da
DS
254 } else {
255 if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
256 vty_out(vty,
257 "Specified a v6 LL with no interface, rejecting\n");
258 return CMD_WARNING_CONFIG_FAILED;
259 }
e5c83d9b 260 nhop.type = NEXTHOP_TYPE_IPV6;
cafec8da 261 }
e5c83d9b
DS
262 }
263
264 if (pbrms->nhg)
265 nh = nexthop_exists(pbrms->nhg, &nhop);
266 else {
267 char buf[100];
268
269 if (no) {
270 vty_out(vty, "No nexthops to delete");
3a9210c2 271 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
272 }
273
274 pbrms->nhg = nexthop_group_new();
275 pbrms->internal_nhg_name =
276 XSTRDUP(MTYPE_TMP,
277 pbr_nht_nexthop_make_name(pbrms->parent->name,
278 PBR_MAP_NAMELEN,
279 pbrms->seqno,
280 buf));
281 nh = NULL;
282 }
283
284 if (no) {
b13e5ad6
DS
285 if (nh)
286 pbr_nht_delete_individual_nexthop(pbrms);
e5c83d9b
DS
287 } else if (!nh) {
288
289 if (pbrms->nhg->nexthop) {
290 vty_out(vty,
291 "If you would like more than one nexthop please use nexthop-groups");
3a9210c2 292 return CMD_WARNING_CONFIG_FAILED;
e5c83d9b
DS
293 }
294
295 /* must be adding new nexthop since !no and !nexthop_exists */
296 nh = nexthop_new();
297
298 memcpy(nh, &nhop, sizeof(nhop));
299 nexthop_add(&pbrms->nhg->nexthop, nh);
300
b13e5ad6
DS
301 pbr_nht_add_individual_nexthop(pbrms);
302 pbr_map_check(pbrms);
e5c83d9b
DS
303 }
304
305 return CMD_SUCCESS;
306}
307
e5c83d9b
DS
308DEFPY (pbr_policy,
309 pbr_policy_cmd,
310 "[no] pbr-policy NAME$mapname",
311 NO_STR
312 "Policy to use\n"
313 "Name of the pbr-map to apply\n")
314{
315 VTY_DECLVAR_CONTEXT(interface, ifp);
316 struct pbr_map *pbrm, *old_pbrm;
317 struct pbr_interface *pbr_ifp = ifp->info;
318
319 pbrm = pbrm_find(mapname);
320
b13e5ad6
DS
321 if (!pbr_ifp) {
322 /*
323 * Some one could have fat fingered the interface
324 * name
325 */
326 pbr_ifp = pbr_if_new(ifp);
327 }
328
e5c83d9b
DS
329 if (no) {
330 if (strcmp(pbr_ifp->mapname, mapname) == 0) {
331 strcpy(pbr_ifp->mapname, "");
332
333 if (pbrm)
334 pbr_map_interface_delete(pbrm, ifp);
335 }
336 } else {
337 if (strcmp(pbr_ifp->mapname, "") == 0) {
338 strcpy(pbr_ifp->mapname, mapname);
339
340 if (pbrm)
341 pbr_map_add_interface(pbrm, ifp);
342 } else {
343 if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) {
344 old_pbrm = pbrm_find(pbr_ifp->mapname);
345 if (old_pbrm)
346 pbr_map_interface_delete(old_pbrm, ifp);
347 strcpy(pbr_ifp->mapname, mapname);
348 if (pbrm)
349 pbr_map_add_interface(pbrm, ifp);
350 }
351 }
352 }
353
354 return CMD_SUCCESS;
355}
356
357DEFPY (show_pbr,
358 show_pbr_cmd,
359 "show pbr [json$json]",
360 SHOW_STR
361 "Policy Based Routing\n"
362 JSON_STR)
363{
364 pbr_nht_write_table_range(vty);
365 pbr_nht_write_rule_range(vty);
366
367 return CMD_SUCCESS;
368}
369
370DEFPY (show_pbr_map,
371 show_pbr_map_cmd,
372 "show pbr map [NAME$name] [detail$detail] [json$json]",
373 SHOW_STR
374 "Policy Based Routing\n"
375 "PBR Map\n"
376 "PBR Map Name\n"
377 "Detailed information\n"
378 JSON_STR)
379{
380 struct pbr_map_sequence *pbrms;
381 struct pbr_map *pbrm;
382 struct listnode *node;
383 char buf[PREFIX_STRLEN];
384 char rbuf[64];
385
386 RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
387 if (name && strcmp(name, pbrm->name) != 0)
388 continue;
389
390 vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name,
391 pbrm->valid);
392
393 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
394 if (pbrms->reason)
395 pbr_map_reason_string(pbrms->reason, rbuf,
396 sizeof(rbuf));
397 vty_out(vty,
37c606ff 398 " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
e5c83d9b
DS
399 pbrms->seqno, pbrms->ruleno, pbrms->installed,
400 pbrms->unique, pbrms->reason ? rbuf : "Valid");
401
402 if (pbrms->src)
403 vty_out(vty, "\tSRC Match: %s\n",
404 prefix2str(pbrms->src, buf,
405 sizeof(buf)));
406 if (pbrms->dst)
407 vty_out(vty, "\tDST Match: %s\n",
408 prefix2str(pbrms->dst, buf,
409 sizeof(buf)));
410
411 if (pbrms->nhgrp_name) {
412 vty_out(vty,
413 "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
414 pbrms->nhgrp_name,
415 pbr_nht_get_table(pbrms->nhgrp_name),
416 pbrms->nhs_installed,
417 pbr_nht_get_installed(
418 pbrms->nhgrp_name));
419 } else if (pbrms->nhg) {
420 vty_out(vty, " ");
421 nexthop_group_write_nexthop(
422 vty, pbrms->nhg->nexthop);
423 vty_out(vty,
424 "\tInstalled: %u(%d) Tableid: %d\n",
425 pbrms->nhs_installed,
426 pbr_nht_get_installed(
427 pbrms->internal_nhg_name),
428 pbr_nht_get_table(
429 pbrms->internal_nhg_name));
430 } else {
431 vty_out(vty,
432 "\tNexthop-Group: Unknown Installed: 0(0)\n");
433 }
434 }
435 }
436 return CMD_SUCCESS;
437}
438
439DEFPY(show_pbr_nexthop_group,
440 show_pbr_nexthop_group_cmd,
441 "show pbr nexthop-groups [WORD$word]",
442 SHOW_STR
443 "Policy Based Routing\n"
444 "Nexthop Groups\n"
445 "Optional Name of the nexthop group\n")
446{
447 pbr_nht_show_nexthop_group(vty, word);
448
449 return CMD_SUCCESS;
450}
451
452DEFPY (show_pbr_interface,
453 show_pbr_interface_cmd,
454 "show pbr interface [NAME$name] [json$json]",
455 SHOW_STR
456 "Policy Based Routing\n"
457 "PBR Interface\n"
458 "PBR Interface Name\n"
459 JSON_STR)
460{
d3765386 461 struct interface *ifp;
e5c83d9b
DS
462 struct vrf *vrf;
463 struct pbr_interface *pbr_ifp;
464
465 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
466 FOR_ALL_INTERFACES(vrf, ifp) {
467 struct pbr_map *pbrm;
468
1c33fb1d
DS
469 if (!ifp->info)
470 continue;
471
e5c83d9b
DS
472 if (name && strcmp(ifp->name, name) != 0)
473 continue;
474
475 pbr_ifp = ifp->info;
476
477 if (strcmp(pbr_ifp->mapname, "") == 0)
478 continue;
479
480 pbrm = pbrm_find(pbr_ifp->mapname);
481 vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
482 ifp->ifindex, pbr_ifp->mapname);
483 if (!pbrm)
484 vty_out(vty, " (map doesn't exist)");
485 vty_out(vty, "\n");
486 }
487 }
488
489 return CMD_SUCCESS;
490}
491
492static struct cmd_node interface_node = {
493 INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
494};
495
496static int pbr_interface_config_write(struct vty *vty)
497{
498 struct interface *ifp;
499 struct vrf *vrf;
500
501 RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
502 FOR_ALL_INTERFACES (vrf, ifp) {
503 if (vrf->vrf_id == VRF_DEFAULT)
504 vty_frame(vty, "interface %s\n", ifp->name);
505 else
506 vty_frame(vty, "interface %s vrf %s\n",
507 ifp->name, vrf->name);
508
509 pbr_map_write_interfaces(vty, ifp);
510
511 vty_endframe(vty, "!\n");
512 }
513 }
514
515 return 1;
516}
517
518/* PBR map node structure. */
519static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1};
520
521static int pbr_vty_map_config_write_sequence(struct vty *vty,
522 struct pbr_map *pbrm,
523 struct pbr_map_sequence *pbrms)
524{
525 char buff[PREFIX_STRLEN];
526
5e44f18f 527 vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
e5c83d9b
DS
528
529 if (pbrms->src)
530 vty_out(vty, " match src-ip %s\n",
d3765386 531 prefix2str(pbrms->src, buff, sizeof(buff)));
e5c83d9b
DS
532
533 if (pbrms->dst)
534 vty_out(vty, " match dst-ip %s\n",
d3765386 535 prefix2str(pbrms->dst, buff, sizeof(buff)));
e5c83d9b
DS
536
537 if (pbrms->nhgrp_name)
538 vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
539
540 if (pbrms->nhg) {
57cdafc4 541 vty_out(vty, " set ");
e5c83d9b
DS
542 nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
543 }
544
5e44f18f 545 vty_out(vty, "!\n");
e5c83d9b
DS
546 return 1;
547}
548
549static int pbr_vty_map_config_write(struct vty *vty)
550{
551 struct pbr_map *pbrm;
552
553 pbr_nht_write_table_range(vty);
554 pbr_nht_write_rule_range(vty);
555
556 RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) {
557 struct pbr_map_sequence *pbrms;
558 struct listnode *node;
559
d3765386 560 for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
e5c83d9b 561 pbr_vty_map_config_write_sequence(vty, pbrm, pbrms);
e5c83d9b
DS
562 }
563
564 return 1;
565}
566
567void pbr_vty_init(void)
568{
569 install_node(&interface_node,
570 pbr_interface_config_write);
571 if_cmd_init();
572
573 install_node(&pbr_map_node,
574 pbr_vty_map_config_write);
575
576 install_default(PBRMAP_NODE);
577
578 install_element(CONFIG_NODE, &pbr_map_cmd);
579 install_element(CONFIG_NODE, &no_pbr_map_cmd);
580 install_element(INTERFACE_NODE, &pbr_policy_cmd);
e5c83d9b
DS
581 install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
582 install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
583 install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
584 install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
585 install_element(VIEW_NODE, &show_pbr_cmd);
586 install_element(VIEW_NODE, &show_pbr_map_cmd);
587 install_element(VIEW_NODE, &show_pbr_interface_cmd);
588 install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
589
590 pbr_debug_init_vty();
e5c83d9b 591}