]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_flowspec_vty.c
Merge pull request #12818 from imzyxwvu/fix/other-table-inactive
[mirror_frr.git] / bgpd / bgp_flowspec_vty.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* BGP FlowSpec VTY
3 * Copyright (C) 2018 6WIND
4 */
5
6 #include <zebra.h>
7 #include "command.h"
8
9 #include "bgpd/bgpd.h"
10 #include "bgpd/bgp_table.h"
11 #include "bgpd/bgp_attr.h"
12 #include "bgpd/bgp_ecommunity.h"
13 #include "bgpd/bgp_vty.h"
14 #include "bgpd/bgp_route.h"
15 #include "bgpd/bgp_aspath.h"
16 #include "bgpd/bgp_flowspec.h"
17 #include "bgpd/bgp_flowspec_util.h"
18 #include "bgpd/bgp_flowspec_private.h"
19 #include "bgpd/bgp_debug.h"
20 #include "bgpd/bgp_pbr.h"
21
22 /* Local Structures and variables declarations
23 * This code block hosts the struct declared that host the flowspec rules
24 * as well as some structure used to convert to stringx
25 */
26
27 static const struct message bgp_flowspec_display_large[] = {
28 {FLOWSPEC_DEST_PREFIX, "Destination Address"},
29 {FLOWSPEC_SRC_PREFIX, "Source Address"},
30 {FLOWSPEC_IP_PROTOCOL, "IP Protocol"},
31 {FLOWSPEC_PORT, "Port"},
32 {FLOWSPEC_DEST_PORT, "Destination Port"},
33 {FLOWSPEC_SRC_PORT, "Source Port"},
34 {FLOWSPEC_ICMP_TYPE, "ICMP Type"},
35 {FLOWSPEC_ICMP_CODE, "ICMP Code"},
36 {FLOWSPEC_TCP_FLAGS, "TCP Flags"},
37 {FLOWSPEC_PKT_LEN, "Packet Length"},
38 {FLOWSPEC_DSCP, "DSCP field"},
39 {FLOWSPEC_FRAGMENT, "Packet Fragment"},
40 {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"},
41 {0}
42 };
43
44 static const struct message bgp_flowspec_display_min[] = {
45 {FLOWSPEC_DEST_PREFIX, "to"},
46 {FLOWSPEC_SRC_PREFIX, "from"},
47 {FLOWSPEC_IP_PROTOCOL, "proto"},
48 {FLOWSPEC_PORT, "port"},
49 {FLOWSPEC_DEST_PORT, "dstp"},
50 {FLOWSPEC_SRC_PORT, "srcp"},
51 {FLOWSPEC_ICMP_TYPE, "type"},
52 {FLOWSPEC_ICMP_CODE, "code"},
53 {FLOWSPEC_TCP_FLAGS, "tcp"},
54 {FLOWSPEC_PKT_LEN, "pktlen"},
55 {FLOWSPEC_DSCP, "dscp"},
56 {FLOWSPEC_FRAGMENT, "pktfrag"},
57 {FLOWSPEC_FLOW_LABEL, "flwlbl"},
58 {0}
59 };
60
61 #define FS_STRING_UPDATE(count, ptr, format, remaining_len) do { \
62 int _len_written; \
63 \
64 if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) {\
65 _len_written = snprintf((ptr), (remaining_len), \
66 ", "); \
67 (remaining_len) -= _len_written; \
68 (ptr) += _len_written; \
69 } else if (((format) == NLRI_STRING_FORMAT_MIN) \
70 && (count)) { \
71 _len_written = snprintf((ptr), (remaining_len), \
72 " "); \
73 (remaining_len) -= _len_written; \
74 (ptr) += _len_written; \
75 } \
76 count++; \
77 } while (0)
78
79 /* Parse FLOWSPEC NLRI
80 * passed return_string string has assumed length
81 * BGP_FLOWSPEC_STRING_DISPLAY_MAX
82 */
83 void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
84 char *return_string, int format,
85 json_object *json_path,
86 afi_t afi)
87 {
88 uint32_t offset = 0;
89 int type;
90 int ret = 0, error = 0;
91 char *ptr = return_string;
92 char local_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
93 int count = 0;
94 char extra[2] = "";
95 char pre_extra[2] = "";
96 const struct message *bgp_flowspec_display;
97 enum bgp_flowspec_util_nlri_t type_util;
98 int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
99 int len_written;
100
101 if (format == NLRI_STRING_FORMAT_LARGE) {
102 snprintf(pre_extra, sizeof(pre_extra), "\t");
103 snprintf(extra, sizeof(extra), "\n");
104 bgp_flowspec_display = bgp_flowspec_display_large;
105 } else
106 bgp_flowspec_display = bgp_flowspec_display_min;
107 /* if needed. type_util can be set to other values */
108 type_util = BGP_FLOWSPEC_RETURN_STRING;
109 error = 0;
110 while (offset < len-1 && error >= 0) {
111 type = nlri_content[offset];
112 offset++;
113 switch (type) {
114 case FLOWSPEC_DEST_PREFIX:
115 case FLOWSPEC_SRC_PREFIX:
116 ret = bgp_flowspec_ip_address(
117 type_util,
118 nlri_content+offset,
119 len - offset,
120 local_string, &error,
121 afi, NULL);
122 if (ret <= 0)
123 break;
124 if (json_path) {
125 json_object_string_add(json_path,
126 lookup_msg(bgp_flowspec_display, type, ""),
127 local_string);
128 break;
129 }
130 FS_STRING_UPDATE(count, ptr, format, len_string);
131 len_written = snprintf(ptr, len_string, "%s%s %s%s",
132 pre_extra,
133 lookup_msg(bgp_flowspec_display,
134 type, ""),
135 local_string, extra);
136 len_string -= len_written;
137 ptr += len_written;
138 break;
139 case FLOWSPEC_FLOW_LABEL:
140 case FLOWSPEC_IP_PROTOCOL:
141 case FLOWSPEC_PORT:
142 case FLOWSPEC_DEST_PORT:
143 case FLOWSPEC_SRC_PORT:
144 case FLOWSPEC_ICMP_TYPE:
145 case FLOWSPEC_ICMP_CODE:
146 ret = bgp_flowspec_op_decode(type_util,
147 nlri_content+offset,
148 len - offset,
149 local_string, &error);
150 if (ret <= 0)
151 break;
152 if (json_path) {
153 json_object_string_add(json_path,
154 lookup_msg(bgp_flowspec_display, type, ""),
155 local_string);
156 break;
157 }
158 FS_STRING_UPDATE(count, ptr, format, len_string);
159 len_written = snprintf(ptr, len_string, "%s%s %s%s",
160 pre_extra,
161 lookup_msg(bgp_flowspec_display,
162 type, ""),
163 local_string, extra);
164 len_string -= len_written;
165 ptr += len_written;
166 break;
167 case FLOWSPEC_TCP_FLAGS:
168 ret = bgp_flowspec_bitmask_decode(
169 type_util,
170 nlri_content+offset,
171 len - offset,
172 local_string, &error);
173 if (ret <= 0)
174 break;
175 if (json_path) {
176 json_object_string_add(json_path,
177 lookup_msg(bgp_flowspec_display,
178 type, ""),
179 local_string);
180 break;
181 }
182 FS_STRING_UPDATE(count, ptr, format, len_string);
183 len_written = snprintf(ptr, len_string, "%s%s %s%s",
184 pre_extra,
185 lookup_msg(bgp_flowspec_display,
186 type, ""),
187 local_string, extra);
188 len_string -= len_written;
189 ptr += len_written;
190 break;
191 case FLOWSPEC_PKT_LEN:
192 case FLOWSPEC_DSCP:
193 ret = bgp_flowspec_op_decode(
194 type_util,
195 nlri_content + offset,
196 len - offset, local_string,
197 &error);
198 if (ret <= 0)
199 break;
200 if (json_path) {
201 json_object_string_add(json_path,
202 lookup_msg(bgp_flowspec_display, type, ""),
203 local_string);
204 break;
205 }
206 FS_STRING_UPDATE(count, ptr, format, len_string);
207 len_written = snprintf(ptr, len_string, "%s%s %s%s",
208 pre_extra,
209 lookup_msg(bgp_flowspec_display,
210 type, ""),
211 local_string, extra);
212 len_string -= len_written;
213 ptr += len_written;
214 break;
215 case FLOWSPEC_FRAGMENT:
216 ret = bgp_flowspec_bitmask_decode(
217 type_util,
218 nlri_content+offset,
219 len - offset,
220 local_string, &error);
221 if (ret <= 0)
222 break;
223 if (json_path) {
224 json_object_string_add(json_path,
225 lookup_msg(bgp_flowspec_display,
226 type, ""),
227 local_string);
228 break;
229 }
230 FS_STRING_UPDATE(count, ptr, format, len_string);
231 len_written = snprintf(ptr, len_string, "%s%s %s%s",
232 pre_extra,
233 lookup_msg(bgp_flowspec_display,
234 type, ""),
235 local_string, extra);
236 len_string -= len_written;
237 ptr += len_written;
238 break;
239 default:
240 error = -1;
241 break;
242 }
243 offset += ret;
244 }
245 }
246
247 void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
248 struct bgp_path_info *path, int display,
249 json_object *json_paths)
250 {
251 struct attr *attr;
252 char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
253 char *s1 = NULL, *s2 = NULL;
254 json_object *json_nlri_path = NULL;
255 json_object *json_ecom_path = NULL;
256 json_object *json_time_path = NULL;
257 char timebuf[BGP_UPTIME_LEN];
258 struct ecommunity *ipv6_ecomm = NULL;
259
260 if (p == NULL || p->family != AF_FLOWSPEC)
261 return;
262 if (json_paths) {
263 if (display == NLRI_STRING_FORMAT_JSON)
264 json_nlri_path = json_object_new_object();
265 else
266 json_nlri_path = json_paths;
267 }
268 if (display == NLRI_STRING_FORMAT_LARGE && path)
269 vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n",
270 path->flags);
271 bgp_fs_nlri_get_string((unsigned char *)
272 p->u.prefix_flowspec.ptr,
273 p->u.prefix_flowspec.prefixlen,
274 return_string,
275 display,
276 json_nlri_path,
277 family2afi(p->u.prefix_flowspec
278 .family));
279 if (display == NLRI_STRING_FORMAT_LARGE)
280 vty_out(vty, "%s", return_string);
281 else if (display == NLRI_STRING_FORMAT_DEBUG)
282 vty_out(vty, "%s", return_string);
283 else if (display == NLRI_STRING_FORMAT_MIN)
284 vty_out(vty, " %-30s", return_string);
285 else if (json_paths && display == NLRI_STRING_FORMAT_JSON)
286 json_object_array_add(json_paths, json_nlri_path);
287 if (!path)
288 return;
289
290 if (path->attr)
291 ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(path->attr);
292
293 if (path->attr && (bgp_attr_get_ecommunity(path->attr) || ipv6_ecomm)) {
294 /* Print attribute */
295 attr = path->attr;
296 if (bgp_attr_get_ecommunity(attr))
297 s1 = ecommunity_ecom2str(bgp_attr_get_ecommunity(attr),
298 ECOMMUNITY_FORMAT_ROUTE_MAP,
299 0);
300 if (ipv6_ecomm)
301 s2 = ecommunity_ecom2str(
302 ipv6_ecomm, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
303 if (!s1 && !s2)
304 return;
305 if (display == NLRI_STRING_FORMAT_LARGE)
306 vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "",
307 s2 && s1 ? " " : "", s2 ? s2 : "");
308 else if (display == NLRI_STRING_FORMAT_MIN)
309 vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : "");
310 else if (json_paths) {
311 json_ecom_path = json_object_new_object();
312 if (s1)
313 json_object_string_add(json_ecom_path,
314 "ecomlist", s1);
315 if (s2)
316 json_object_string_add(json_ecom_path,
317 "ecom6list", s2);
318 if (display == NLRI_STRING_FORMAT_JSON)
319 json_object_array_add(json_paths,
320 json_ecom_path);
321 }
322 if (display == NLRI_STRING_FORMAT_LARGE) {
323 char local_buff[INET6_ADDRSTRLEN];
324
325 local_buff[0] = '\0';
326 if (p->u.prefix_flowspec.family == AF_INET
327 && attr->nexthop.s_addr != INADDR_ANY)
328 inet_ntop(AF_INET, &attr->nexthop.s_addr,
329 local_buff, sizeof(local_buff));
330 else if (p->u.prefix_flowspec.family == AF_INET6 &&
331 attr->mp_nexthop_len != 0 &&
332 attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 &&
333 attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4)
334 inet_ntop(AF_INET6, &attr->mp_nexthop_global,
335 local_buff, sizeof(local_buff));
336 if (local_buff[0] != '\0')
337 vty_out(vty, "\tNLRI NH %s\n",
338 local_buff);
339 }
340 XFREE(MTYPE_ECOMMUNITY_STR, s1);
341 XFREE(MTYPE_ECOMMUNITY_STR, s2);
342 }
343 peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
344 if (display == NLRI_STRING_FORMAT_LARGE) {
345 vty_out(vty, "\treceived for %8s\n", timebuf);
346 } else if (json_paths) {
347 json_time_path = json_object_new_object();
348 json_object_string_add(json_time_path,
349 "time", timebuf);
350 if (display == NLRI_STRING_FORMAT_JSON)
351 json_object_array_add(json_paths, json_time_path);
352 }
353 if (display == NLRI_STRING_FORMAT_LARGE) {
354 struct bgp_path_info_extra *extra =
355 bgp_path_info_extra_get(path);
356 bool list_began = false;
357
358 if (extra->bgp_fs_pbr && listcount(extra->bgp_fs_pbr)) {
359 struct listnode *node;
360 struct bgp_pbr_match_entry *bpme;
361 struct bgp_pbr_match *bpm;
362 struct list *list_bpm;
363
364 list_bpm = list_new();
365 vty_out(vty, "\tinstalled in PBR");
366 for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_pbr,
367 node, bpme)) {
368 bpm = bpme->backpointer;
369 if (listnode_lookup(list_bpm, bpm))
370 continue;
371 listnode_add(list_bpm, bpm);
372 if (!list_began) {
373 vty_out(vty, " (");
374 list_began = true;
375 } else
376 vty_out(vty, ", ");
377 vty_out(vty, "%s", bpm->ipset_name);
378 }
379 list_delete(&list_bpm);
380 }
381 if (extra->bgp_fs_iprule && listcount(extra->bgp_fs_iprule)) {
382 struct listnode *node;
383 struct bgp_pbr_rule *bpr;
384
385 if (!list_began)
386 vty_out(vty, "\tinstalled in PBR");
387 for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_iprule,
388 node, bpr)) {
389 if (!bpr->action)
390 continue;
391 if (!list_began) {
392 vty_out(vty, " (");
393 list_began = true;
394 } else
395 vty_out(vty, ", ");
396 vty_out(vty, "-ipv4-rule %d action lookup %u-",
397 bpr->priority,
398 bpr->action->table_id);
399 }
400 }
401 if (list_began)
402 vty_out(vty, ")\n");
403 else
404 vty_out(vty, "\tnot installed in PBR\n");
405 }
406 }
407
408 int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi,
409 struct bgp_table *table, enum bgp_show_type type,
410 void *output_arg, bool use_json, int is_last,
411 unsigned long *output_cum, unsigned long *total_cum)
412 {
413 struct bgp_path_info *pi;
414 struct bgp_dest *dest;
415 unsigned long total_count = 0;
416 json_object *json_paths = NULL;
417 int display = NLRI_STRING_FORMAT_LARGE;
418
419 if (type != bgp_show_type_detail)
420 return CMD_SUCCESS;
421
422 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
423 pi = bgp_dest_get_bgp_path_info(dest);
424 if (pi == NULL)
425 continue;
426 if (use_json) {
427 json_paths = json_object_new_array();
428 display = NLRI_STRING_FORMAT_JSON;
429 }
430 for (; pi; pi = pi->next) {
431 total_count++;
432 route_vty_out_flowspec(vty, bgp_dest_get_prefix(dest),
433 pi, display, json_paths);
434 }
435 if (use_json) {
436 vty_json(vty, json_paths);
437 json_paths = NULL;
438 }
439 }
440 if (total_count && !use_json)
441 vty_out(vty,
442 "\nDisplayed %ld flowspec entries\n",
443 total_count);
444 return CMD_SUCCESS;
445 }
446
447 DEFUN (debug_bgp_flowspec,
448 debug_bgp_flowspec_cmd,
449 "debug bgp flowspec",
450 DEBUG_STR
451 BGP_STR
452 "BGP allow flowspec debugging entries\n")
453 {
454 if (vty->node == CONFIG_NODE)
455 DEBUG_ON(flowspec, FLOWSPEC);
456 else {
457 TERM_DEBUG_ON(flowspec, FLOWSPEC);
458 vty_out(vty, "BGP flowspec debugging is on\n");
459 }
460 return CMD_SUCCESS;
461 }
462
463 DEFUN (no_debug_bgp_flowspec,
464 no_debug_bgp_flowspec_cmd,
465 "no debug bgp flowspec",
466 NO_STR
467 DEBUG_STR
468 BGP_STR
469 "BGP allow flowspec debugging entries\n")
470 {
471 if (vty->node == CONFIG_NODE)
472 DEBUG_OFF(flowspec, FLOWSPEC);
473 else {
474 TERM_DEBUG_OFF(flowspec, FLOWSPEC);
475 vty_out(vty, "BGP flowspec debugging is off\n");
476 }
477 return CMD_SUCCESS;
478 }
479
480 int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
481 afi_t afi, safi_t safi)
482 {
483 struct bgp_pbr_interface *pbr_if;
484 bool declare_node = false;
485 struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
486 struct bgp_pbr_interface_head *head;
487 bool bgp_pbr_interface_any;
488
489 if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC)
490 return 0;
491 if (afi == AFI_IP) {
492 head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
493 bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4;
494 } else if (afi == AFI_IP6) {
495 head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
496 bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6;
497 } else {
498 return 0;
499 }
500 if (!RB_EMPTY(bgp_pbr_interface_head, head) ||
501 !bgp_pbr_interface_any)
502 declare_node = true;
503 RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
504 vty_out(vty, " local-install %s\n", pbr_if->name);
505 }
506 return declare_node ? 1 : 0;
507 }
508
509 static int bgp_fs_local_install_interface(struct bgp *bgp,
510 const char *no, const char *ifname,
511 afi_t afi)
512 {
513 struct bgp_pbr_interface *pbr_if;
514 struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
515 struct bgp_pbr_interface_head *head;
516 bool *bgp_pbr_interface_any;
517
518 if (!bgp_pbr_cfg)
519 return CMD_SUCCESS;
520 if (afi == AFI_IP) {
521 head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
522 bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4);
523 } else {
524 head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
525 bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6);
526 }
527 if (no) {
528 if (!ifname) {
529 if (*bgp_pbr_interface_any) {
530 *bgp_pbr_interface_any = false;
531 /* remove all other interface list */
532 bgp_pbr_reset(bgp, afi);
533 }
534 return CMD_SUCCESS;
535 }
536 pbr_if = bgp_pbr_interface_lookup(ifname, head);
537 if (!pbr_if)
538 return CMD_SUCCESS;
539 RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
540 return CMD_SUCCESS;
541 }
542 if (ifname) {
543 pbr_if = bgp_pbr_interface_lookup(ifname, head);
544 if (pbr_if)
545 return CMD_SUCCESS;
546 pbr_if = XCALLOC(MTYPE_TMP,
547 sizeof(struct bgp_pbr_interface));
548 strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ);
549 RB_INSERT(bgp_pbr_interface_head, head, pbr_if);
550 *bgp_pbr_interface_any = false;
551 } else {
552 /* set to default */
553 if (!*bgp_pbr_interface_any) {
554 /* remove all other interface list
555 */
556 bgp_pbr_reset(bgp, afi);
557 *bgp_pbr_interface_any = true;
558 }
559 }
560 return CMD_SUCCESS;
561 }
562
563 DEFUN (bgp_fs_local_install_ifname,
564 bgp_fs_local_install_ifname_cmd,
565 "[no] local-install INTERFACE",
566 NO_STR
567 "Apply local policy routing\n"
568 "Interface name\n")
569 {
570 struct bgp *bgp = VTY_GET_CONTEXT(bgp);
571 int idx = 0;
572 const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
573 char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ?
574 argv[idx]->arg : NULL;
575
576 return bgp_fs_local_install_interface(bgp, no, ifname,
577 bgp_node_afi(vty));
578 }
579
580 extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib,
581 struct prefix *match,
582 int prefix_check, struct vty *vty,
583 bool use_json,
584 json_object *json_paths)
585 {
586 struct bgp_dest *dest;
587 const struct prefix *prefix;
588 int display = 0;
589
590 for (dest = bgp_table_top(rib); dest; dest = bgp_route_next(dest)) {
591 prefix = bgp_dest_get_prefix(dest);
592
593 if (prefix->family != AF_FLOWSPEC)
594 continue;
595
596 if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) {
597 route_vty_out_flowspec(
598 vty, prefix, bgp_dest_get_bgp_path_info(dest),
599 use_json ? NLRI_STRING_FORMAT_JSON
600 : NLRI_STRING_FORMAT_LARGE,
601 json_paths);
602 display++;
603 }
604 }
605 return display;
606 }
607
608 void bgp_flowspec_vty_init(void)
609 {
610 install_element(ENABLE_NODE, &debug_bgp_flowspec_cmd);
611 install_element(CONFIG_NODE, &debug_bgp_flowspec_cmd);
612 install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd);
613 install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd);
614 install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd);
615 install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd);
616 }