diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index a8e50e0ba7..61d46dfcb9 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -86,7 +86,8 @@ libbgp_a_SOURCES = \ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \ - bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c + bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \ + bgp_flowspec_vty.c noinst_HEADERS = \ bgp_memory.h \ diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index bf019da3aa..791ed2e693 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -19,9 +19,29 @@ #ifndef _FRR_BGP_FLOWSPEC_H #define _FRR_BGP_FLOWSPEC_H +#define NLRI_STRING_FORMAT_LARGE 0 +#define NLRI_STRING_FORMAT_DEBUG 1 +#define NLRI_STRING_FORMAT_MIN 2 + +#define BGP_FLOWSPEC_NLRI_STRING_MAX 512 + extern int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, struct bgp_nlri *packet, int withdraw); extern void bgp_flowspec_vty_init(void); +extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, + struct bgp_table *table, + enum bgp_show_type type, + void *output_arg, uint8_t use_json, + int is_last, + unsigned long *output_cum, + unsigned long *total_cum); + +extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, + char *return_string, int format); + +extern void route_vty_out_flowspec(struct vty *vty, struct prefix *p, + struct bgp_info *binfo, + int display, json_object *json_paths); #endif /* _FRR_BGP_FLOWSPEC_H */ diff --git a/bgpd/bgp_flowspec_private.h b/bgpd/bgp_flowspec_private.h index 4f086a9f7b..dede4e03d3 100644 --- a/bgpd/bgp_flowspec_private.h +++ b/bgpd/bgp_flowspec_private.h @@ -26,4 +26,19 @@ #define FLOWSPEC_TRAFFIC_ACTION_SAMPLE 0 #define FLOWSPEC_TRAFFIC_ACTION_DISTRIBUTE 1 +/* Flow Spec Component Types */ +#define NUM_OF_FLOWSPEC_MATCH_TYPES 12 +#define FLOWSPEC_DEST_PREFIX 1 +#define FLOWSPEC_SRC_PREFIX 2 +#define FLOWSPEC_IP_PROTOCOL 3 +#define FLOWSPEC_PORT 4 +#define FLOWSPEC_DEST_PORT 5 +#define FLOWSPEC_SRC_PORT 6 +#define FLOWSPEC_ICMP_TYPE 7 +#define FLOWSPEC_ICMP_CODE 8 +#define FLOWSPEC_TCP_FLAGS 9 +#define FLOWSPEC_PKT_LEN 10 +#define FLOWSPEC_DSCP 11 +#define FLOWSPEC_FRAGMENT 12 + #endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c new file mode 100644 index 0000000000..1006022683 --- /dev/null +++ b/bgpd/bgp_flowspec_vty.c @@ -0,0 +1,276 @@ +/* BGP FlowSpec VTY + * Copyright (C) 2018 6WIND + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "command.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_flowspec.h" +#include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_flowspec_private.h" + +/* Local Structures and variables declarations + * This code block hosts the struct declared that host the flowspec rules + * as well as some structure used to convert to stringx + */ + +static const struct message bgp_flowspec_display_large[] = { + {FLOWSPEC_DEST_PREFIX, "Destination Address"}, + {FLOWSPEC_SRC_PREFIX, "Source Address"}, + {FLOWSPEC_IP_PROTOCOL, "IP Protocol"}, + {FLOWSPEC_PORT, "Port"}, + {FLOWSPEC_DEST_PORT, "Destination Port"}, + {FLOWSPEC_SRC_PORT, "Source Port"}, + {FLOWSPEC_ICMP_TYPE, "ICMP Type"}, + {FLOWSPEC_ICMP_CODE, "ICMP Code"}, + {FLOWSPEC_TCP_FLAGS, "TCP Flags"}, + {FLOWSPEC_PKT_LEN, "Packet Length"}, + {FLOWSPEC_DSCP, "DSCP field"}, + {FLOWSPEC_FRAGMENT, "Packet Fragment"}, + {0} +}; + +static const struct message bgp_flowspec_display_min[] = { + {FLOWSPEC_DEST_PREFIX, "to"}, + {FLOWSPEC_SRC_PREFIX, "from"}, + {FLOWSPEC_IP_PROTOCOL, "proto"}, + {FLOWSPEC_PORT, "port"}, + {FLOWSPEC_DEST_PORT, "dstp"}, + {FLOWSPEC_SRC_PORT, "srcp"}, + {FLOWSPEC_ICMP_TYPE, "type"}, + {FLOWSPEC_ICMP_CODE, "code"}, + {FLOWSPEC_TCP_FLAGS, "flags"}, + {FLOWSPEC_PKT_LEN, "pktlen"}, + {FLOWSPEC_DSCP, "dscp"}, + {FLOWSPEC_FRAGMENT, "pktfrag"}, + {0} +}; + +#define FS_STRING_UPDATE(count, ptr, format) do { \ + if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) { \ + (ptr) += sprintf((ptr), ", "); \ + } else if (((format) == NLRI_STRING_FORMAT_MIN) && (count)) { \ + (ptr) += sprintf((ptr), " "); \ + } \ + count++; \ + } while (0) + +/* Parse FLOWSPEC NLRI*/ +void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, + char *return_string, int format) +{ + uint32_t offset = 0; + int type; + int ret = 0, error = 0; + char *ptr = return_string; + char local_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + int count = 0; + char extra[2] = ""; + char pre_extra[2] = ""; + const struct message *bgp_flowspec_display; + + if (format == NLRI_STRING_FORMAT_LARGE) { + snprintf(pre_extra, sizeof(pre_extra), "\t"); + snprintf(extra, sizeof(extra), "\n"); + bgp_flowspec_display = bgp_flowspec_display_large; + } else + bgp_flowspec_display = bgp_flowspec_display_min; + error = 0; + while (offset < len-1 && error >= 0) { + type = nlri_content[offset]; + offset++; + switch (type) { + case FLOWSPEC_DEST_PREFIX: + case FLOWSPEC_SRC_PREFIX: + ret = bgp_flowspec_ip_address( + BGP_FLOWSPEC_RETURN_STRING, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + FS_STRING_UPDATE(count, ptr, format); + ptr += sprintf(ptr, "%s%s %s%s", pre_extra, + lookup_msg(bgp_flowspec_display, type, ""), + local_string, extra); + break; + case FLOWSPEC_IP_PROTOCOL: + case FLOWSPEC_PORT: + case FLOWSPEC_DEST_PORT: + case FLOWSPEC_SRC_PORT: + case FLOWSPEC_ICMP_TYPE: + case FLOWSPEC_ICMP_CODE: + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_RETURN_STRING, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + FS_STRING_UPDATE(count, ptr, format); + ptr += sprintf(ptr, "%s%s %s%s", pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + BGP_FLOWSPEC_RETURN_STRING, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + FS_STRING_UPDATE(count, ptr, format); + ptr += sprintf(ptr, "%s%s %s%s", pre_extra, + lookup_msg(bgp_flowspec_display, type, ""), + local_string, extra); + break; + case FLOWSPEC_PKT_LEN: + case FLOWSPEC_DSCP: + ret = bgp_flowspec_op_decode( + BGP_FLOWSPEC_RETURN_STRING, + nlri_content + offset, + len - offset, local_string, + &error); + if (ret <= 0) + break; + FS_STRING_UPDATE(count, ptr, format); + ptr += sprintf(ptr, "%s%s %s%s", pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + break; + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_fragment_type_decode( + BGP_FLOWSPEC_RETURN_STRING, + nlri_content + offset, + len - offset, local_string, + &error); + if (ret <= 0) + break; + FS_STRING_UPDATE(count, ptr, format); + ptr += sprintf(ptr, "%s%s %s%s", pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + break; + default: + error = -1; + break; + } + offset += ret; + } +} + +void route_vty_out_flowspec(struct vty *vty, struct prefix *p, + struct bgp_info *binfo, + int display, json_object *json_paths) +{ + struct attr *attr; + char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + char *s; + + /* Print prefix */ + if (p != NULL) { + if (p->family != AF_FLOWSPEC) + return; + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", + binfo->flags); + bgp_fs_nlri_get_string((unsigned char *) + p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, + return_string, + display); + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_DEBUG) + vty_out(vty, "%s", return_string); + else + vty_out(vty, " %-30s", return_string); + } + if (!binfo) + return; + if (binfo->attr && binfo->attr->ecommunity) { + /* Print attribute */ + attr = binfo->attr; + s = ecommunity_ecom2str(attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (!s) + return; + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "\t%s\n", s); + else + vty_out(vty, "%s", s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + if (display == NLRI_STRING_FORMAT_LARGE) { + char timebuf[BGP_UPTIME_LEN]; + + vty_out(vty, "\tup for %8s\n", + peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, + 0, NULL)); + } + +} + +int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, + struct bgp_table *table, enum bgp_show_type type, + void *output_arg, uint8_t use_json, + int is_last, unsigned long *output_cum, + unsigned long *total_cum) +{ + struct bgp_info *ri; + struct bgp_node *rn; + unsigned long total_count = 0; + json_object *json_paths = NULL; + int display; + + if (type != bgp_show_type_detail) + return CMD_SUCCESS; + + display = NLRI_STRING_FORMAT_LARGE; + if (use_json) /* XXX */ + return CMD_SUCCESS; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + if (rn->info == NULL) + continue; + for (ri = rn->info; ri; ri = ri->next) { + total_count++; + route_vty_out_flowspec(vty, &rn->p, + ri, display, + json_paths); + + } + } + if (total_count) + vty_out(vty, + "\nDisplayed %ld flowspec entries\n", + total_count); + return CMD_SUCCESS; +} + +void bgp_flowspec_vty_init(void) +{ +} diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index aef3ad6a7b..db82386e72 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -73,6 +73,7 @@ #include "bgpd/bgp_encap_tlv.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_vty.h" +#include "bgpd/bgp_flowspec.h" #ifndef VTYSH_EXTRACT_PL #include "bgpd/bgp_route_clippy.c" @@ -6316,6 +6317,9 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty, prefix2str(p, buf, PREFIX_STRLEN); len = vty_out(vty, "%s", buf); #endif + } else if (p->family == AF_FLOWSPEC) { + route_vty_out_flowspec(vty, p, NULL, + NLRI_STRING_FORMAT_MIN, json); } else { if (!json) len = vty_out( @@ -8429,6 +8433,12 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, return bgp_show_table_rd(vty, bgp, safi, table, NULL, type, output_arg, use_json); } + + if (safi == SAFI_FLOWSPEC && type == bgp_show_type_detail) { + return bgp_show_table_flowspec(vty, bgp, afi, table, type, + output_arg, use_json, + 1, NULL, NULL); + } /* labeled-unicast routes live in the unicast table */ else if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; @@ -10425,6 +10435,32 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json); } +DEFUN (show_ip_bgp_flowspec_routes_detailed, + show_ip_bgp_flowspec_routes_detailed_cmd, + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" flowspec] detail [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + "SAFI Flowspec\n" + "Detailed information on flowspec entries\n" + JSON_STR) +{ + afi_t afi = AFI_IP; + safi_t safi = SAFI_UNICAST; + struct bgp *bgp = NULL; + int idx = 0; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp); + if (!idx) + return CMD_WARNING; + + return bgp_show(vty, bgp, afi, safi, + bgp_show_type_detail, NULL, use_json(argc, argv)); +} + DEFUN (show_ip_bgp_neighbor_routes, show_ip_bgp_neighbor_routes_cmd, "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] " @@ -11439,6 +11475,10 @@ void bgp_route_init(void) /* Large Communities */ install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd); install_element(VIEW_NODE, &show_ip_bgp_large_community_cmd); + + /* show bgp ipv4 flowspec detailed */ + install_element(VIEW_NODE, &show_ip_bgp_flowspec_routes_detailed_cmd); + } void bgp_route_finish(void) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index e1e43bbde3..debd6d1ff3 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -48,7 +48,8 @@ enum bgp_show_type { bgp_show_type_flap_statistics, bgp_show_type_flap_neighbor, bgp_show_type_dampend_paths, - bgp_show_type_damp_neighbor + bgp_show_type_damp_neighbor, + bgp_show_type_detail, };