1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018 6WIND
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"
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
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"},
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"},
61 #define FS_STRING_UPDATE(count, ptr, format, remaining_len) do { \
64 if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) {\
65 _len_written = snprintf((ptr), (remaining_len), \
67 (remaining_len) -= _len_written; \
68 (ptr) += _len_written; \
69 } else if (((format) == NLRI_STRING_FORMAT_MIN) \
71 _len_written = snprintf((ptr), (remaining_len), \
73 (remaining_len) -= _len_written; \
74 (ptr) += _len_written; \
79 /* Parse FLOWSPEC NLRI
80 * passed return_string string has assumed length
81 * BGP_FLOWSPEC_STRING_DISPLAY_MAX
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
,
90 int ret
= 0, error
= 0;
91 char *ptr
= return_string
;
92 char local_string
[BGP_FLOWSPEC_STRING_DISPLAY_MAX
];
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
;
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
;
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
;
110 while (offset
< len
-1 && error
>= 0) {
111 type
= nlri_content
[offset
];
114 case FLOWSPEC_DEST_PREFIX
:
115 case FLOWSPEC_SRC_PREFIX
:
116 ret
= bgp_flowspec_ip_address(
120 local_string
, &error
,
125 json_object_string_add(json_path
,
126 lookup_msg(bgp_flowspec_display
, type
, ""),
130 FS_STRING_UPDATE(count
, ptr
, format
, len_string
);
131 len_written
= snprintf(ptr
, len_string
, "%s%s %s%s",
133 lookup_msg(bgp_flowspec_display
,
135 local_string
, extra
);
136 len_string
-= len_written
;
139 case FLOWSPEC_FLOW_LABEL
:
140 case FLOWSPEC_IP_PROTOCOL
:
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
,
149 local_string
, &error
);
153 json_object_string_add(json_path
,
154 lookup_msg(bgp_flowspec_display
, type
, ""),
158 FS_STRING_UPDATE(count
, ptr
, format
, len_string
);
159 len_written
= snprintf(ptr
, len_string
, "%s%s %s%s",
161 lookup_msg(bgp_flowspec_display
,
163 local_string
, extra
);
164 len_string
-= len_written
;
167 case FLOWSPEC_TCP_FLAGS
:
168 ret
= bgp_flowspec_bitmask_decode(
172 local_string
, &error
);
176 json_object_string_add(json_path
,
177 lookup_msg(bgp_flowspec_display
,
182 FS_STRING_UPDATE(count
, ptr
, format
, len_string
);
183 len_written
= snprintf(ptr
, len_string
, "%s%s %s%s",
185 lookup_msg(bgp_flowspec_display
,
187 local_string
, extra
);
188 len_string
-= len_written
;
191 case FLOWSPEC_PKT_LEN
:
193 ret
= bgp_flowspec_op_decode(
195 nlri_content
+ offset
,
196 len
- offset
, local_string
,
201 json_object_string_add(json_path
,
202 lookup_msg(bgp_flowspec_display
, type
, ""),
206 FS_STRING_UPDATE(count
, ptr
, format
, len_string
);
207 len_written
= snprintf(ptr
, len_string
, "%s%s %s%s",
209 lookup_msg(bgp_flowspec_display
,
211 local_string
, extra
);
212 len_string
-= len_written
;
215 case FLOWSPEC_FRAGMENT
:
216 ret
= bgp_flowspec_bitmask_decode(
220 local_string
, &error
);
224 json_object_string_add(json_path
,
225 lookup_msg(bgp_flowspec_display
,
230 FS_STRING_UPDATE(count
, ptr
, format
, len_string
);
231 len_written
= snprintf(ptr
, len_string
, "%s%s %s%s",
233 lookup_msg(bgp_flowspec_display
,
235 local_string
, extra
);
236 len_string
-= len_written
;
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
)
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
;
260 if (p
== NULL
|| p
->family
!= AF_FLOWSPEC
)
263 if (display
== NLRI_STRING_FORMAT_JSON
)
264 json_nlri_path
= json_object_new_object();
266 json_nlri_path
= json_paths
;
268 if (display
== NLRI_STRING_FORMAT_LARGE
&& path
)
269 vty_out(vty
, "BGP flowspec entry: (flags 0x%x)\n",
271 bgp_fs_nlri_get_string((unsigned char *)
272 p
->u
.prefix_flowspec
.ptr
,
273 p
->u
.prefix_flowspec
.prefixlen
,
277 family2afi(p
->u
.prefix_flowspec
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
);
291 ipv6_ecomm
= bgp_attr_get_ipv6_ecommunity(path
->attr
);
293 if (path
->attr
&& (bgp_attr_get_ecommunity(path
->attr
) || ipv6_ecomm
)) {
294 /* Print attribute */
296 if (bgp_attr_get_ecommunity(attr
))
297 s1
= ecommunity_ecom2str(bgp_attr_get_ecommunity(attr
),
298 ECOMMUNITY_FORMAT_ROUTE_MAP
,
301 s2
= ecommunity_ecom2str(
302 ipv6_ecomm
, ECOMMUNITY_FORMAT_ROUTE_MAP
, 0);
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();
313 json_object_string_add(json_ecom_path
,
316 json_object_string_add(json_ecom_path
,
318 if (display
== NLRI_STRING_FORMAT_JSON
)
319 json_object_array_add(json_paths
,
322 if (display
== NLRI_STRING_FORMAT_LARGE
) {
323 char local_buff
[INET6_ADDRSTRLEN
];
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",
340 XFREE(MTYPE_ECOMMUNITY_STR
, s1
);
341 XFREE(MTYPE_ECOMMUNITY_STR
, s2
);
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
,
350 if (display
== NLRI_STRING_FORMAT_JSON
)
351 json_object_array_add(json_paths
, json_time_path
);
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;
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
;
364 list_bpm
= list_new();
365 vty_out(vty
, "\tinstalled in PBR");
366 for (ALL_LIST_ELEMENTS_RO(extra
->bgp_fs_pbr
,
368 bpm
= bpme
->backpointer
;
369 if (listnode_lookup(list_bpm
, bpm
))
371 listnode_add(list_bpm
, bpm
);
377 vty_out(vty
, "%s", bpm
->ipset_name
);
379 list_delete(&list_bpm
);
381 if (extra
->bgp_fs_iprule
&& listcount(extra
->bgp_fs_iprule
)) {
382 struct listnode
*node
;
383 struct bgp_pbr_rule
*bpr
;
386 vty_out(vty
, "\tinstalled in PBR");
387 for (ALL_LIST_ELEMENTS_RO(extra
->bgp_fs_iprule
,
396 vty_out(vty
, "-ipv4-rule %d action lookup %u-",
398 bpr
->action
->table_id
);
404 vty_out(vty
, "\tnot installed in PBR\n");
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
)
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
;
419 if (type
!= bgp_show_type_detail
)
422 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
423 pi
= bgp_dest_get_bgp_path_info(dest
);
427 json_paths
= json_object_new_array();
428 display
= NLRI_STRING_FORMAT_JSON
;
430 for (; pi
; pi
= pi
->next
) {
432 route_vty_out_flowspec(vty
, bgp_dest_get_prefix(dest
),
433 pi
, display
, json_paths
);
436 vty_json(vty
, json_paths
);
440 if (total_count
&& !use_json
)
442 "\nDisplayed %ld flowspec entries\n",
447 DEFUN (debug_bgp_flowspec
,
448 debug_bgp_flowspec_cmd
,
449 "debug bgp flowspec",
452 "BGP allow flowspec debugging entries\n")
454 if (vty
->node
== CONFIG_NODE
)
455 DEBUG_ON(flowspec
, FLOWSPEC
);
457 TERM_DEBUG_ON(flowspec
, FLOWSPEC
);
458 vty_out(vty
, "BGP flowspec debugging is on\n");
463 DEFUN (no_debug_bgp_flowspec
,
464 no_debug_bgp_flowspec_cmd
,
465 "no debug bgp flowspec",
469 "BGP allow flowspec debugging entries\n")
471 if (vty
->node
== CONFIG_NODE
)
472 DEBUG_OFF(flowspec
, FLOWSPEC
);
474 TERM_DEBUG_OFF(flowspec
, FLOWSPEC
);
475 vty_out(vty
, "BGP flowspec debugging is off\n");
480 int bgp_fs_config_write_pbr(struct vty
*vty
, struct bgp
*bgp
,
481 afi_t afi
, safi_t safi
)
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
;
489 if (!bgp_pbr_cfg
|| safi
!= SAFI_FLOWSPEC
)
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
;
500 if (!RB_EMPTY(bgp_pbr_interface_head
, head
) ||
501 !bgp_pbr_interface_any
)
503 RB_FOREACH (pbr_if
, bgp_pbr_interface_head
, head
) {
504 vty_out(vty
, " local-install %s\n", pbr_if
->name
);
506 return declare_node
? 1 : 0;
509 static int bgp_fs_local_install_interface(struct bgp
*bgp
,
510 const char *no
, const char *ifname
,
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
;
521 head
= &(bgp_pbr_cfg
->ifaces_by_name_ipv4
);
522 bgp_pbr_interface_any
= &(bgp_pbr_cfg
->pbr_interface_any_ipv4
);
524 head
= &(bgp_pbr_cfg
->ifaces_by_name_ipv6
);
525 bgp_pbr_interface_any
= &(bgp_pbr_cfg
->pbr_interface_any_ipv6
);
529 if (*bgp_pbr_interface_any
) {
530 *bgp_pbr_interface_any
= false;
531 /* remove all other interface list */
532 bgp_pbr_reset(bgp
, afi
);
536 pbr_if
= bgp_pbr_interface_lookup(ifname
, head
);
539 RB_REMOVE(bgp_pbr_interface_head
, head
, pbr_if
);
543 pbr_if
= bgp_pbr_interface_lookup(ifname
, head
);
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;
553 if (!*bgp_pbr_interface_any
) {
554 /* remove all other interface list
556 bgp_pbr_reset(bgp
, afi
);
557 *bgp_pbr_interface_any
= true;
563 DEFUN (bgp_fs_local_install_ifname
,
564 bgp_fs_local_install_ifname_cmd
,
565 "[no] local-install INTERFACE",
567 "Apply local policy routing\n"
570 struct bgp
*bgp
= VTY_GET_CONTEXT(bgp
);
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
;
576 return bgp_fs_local_install_interface(bgp
, no
, ifname
,
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
,
584 json_object
*json_paths
)
586 struct bgp_dest
*dest
;
587 const struct prefix
*prefix
;
590 for (dest
= bgp_table_top(rib
); dest
; dest
= bgp_route_next(dest
)) {
591 prefix
= bgp_dest_get_prefix(dest
);
593 if (prefix
->family
!= AF_FLOWSPEC
)
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
,
608 void bgp_flowspec_vty_init(void)
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
);