diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 5e08f82774..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_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \ + bgp_flowspec_vty.c noinst_HEADERS = \ bgp_memory.h \ @@ -99,7 +100,7 @@ noinst_HEADERS = \ bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \ $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \ - bgp_io.h + bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 58788a8959..ef839dba6a 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -52,6 +52,7 @@ #endif #include "bgp_encap_types.h" #include "bgp_evpn.h" +#include "bgp_flowspec_private.h" /* Attribute strings for logging. */ static const struct message attr_str[] = { @@ -1647,6 +1648,13 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, /* Nexthop length check. */ switch (attr->mp_nexthop_len) { + case 0: + if (safi != SAFI_FLOWSPEC) { + zlog_info("%s: (%s) Wrong multiprotocol next hop length: %d", + __func__, peer->host, attr->mp_nexthop_len); + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; + } + break; case BGP_ATTR_NHLEN_VPNV4: stream_getl(s); /* RD high */ stream_getl(s); /* RD low */ @@ -2669,6 +2677,8 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, stream_putc(s, 4); stream_put(s, &attr->mp_nexthop_global_in, 4); break; + case SAFI_FLOWSPEC: + stream_putc(s, 0); /* no nexthop for flowspec */ default: break; } @@ -2719,14 +2729,17 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, stream_put(s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN); break; + case SAFI_FLOWSPEC: + stream_putc(s, 0); /* no nexthop for flowspec */ default: break; } break; default: - zlog_err( - "Bad nexthop when sening to %s, AFI %u SAFI %u nhlen %d", - peer->host, afi, safi, attr->mp_nexthop_len); + if (safi != SAFI_FLOWSPEC) + zlog_err( + "Bad nexthop when sending to %s, AFI %u SAFI %u nhlen %d", + peer->host, afi, safi, attr->mp_nexthop_len); break; } @@ -2756,6 +2769,14 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, } else if (safi == SAFI_LABELED_UNICAST) { /* Prefix write with label. */ stream_put_labeled_prefix(s, p, label); + } else if (safi == SAFI_FLOWSPEC) { + if (PSIZE (p->prefixlen)+2 < FLOWSPEC_NLRI_SIZELIMIT) + stream_putc(s, PSIZE (p->prefixlen)+2); + else + stream_putw(s, (PSIZE (p->prefixlen)+2)|(0xf<<12)); + stream_putc(s, 2);/* Filter type */ + stream_putc(s, p->prefixlen);/* Prefix length */ + stream_put(s, &p->u.prefix, PSIZE (p->prefixlen)); } else stream_put_prefix_addpath(s, p, addpath_encode, addpath_tx_id); } diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 54fcd47e4b..ae4ff5d67e 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -42,6 +42,7 @@ #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_label.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_flowspec.h" unsigned long conf_bgp_debug_as4; unsigned long conf_bgp_debug_neighbor_events; @@ -56,6 +57,7 @@ unsigned long conf_bgp_debug_allow_martians; unsigned long conf_bgp_debug_nht; unsigned long conf_bgp_debug_update_groups; unsigned long conf_bgp_debug_vpn; +unsigned long conf_bgp_debug_flowspec; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -70,6 +72,7 @@ unsigned long term_bgp_debug_allow_martians; unsigned long term_bgp_debug_nht; unsigned long term_bgp_debug_update_groups; unsigned long term_bgp_debug_vpn; +unsigned long term_bgp_debug_flowspec; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -1688,6 +1691,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL); + TERM_DEBUG_OFF(flowspec, FLOWSPEC); vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -1758,6 +1762,8 @@ DEFUN_NOSH (show_debugging_bgp, vty_out(vty, " BGP vpn route-map event debugging is on\n"); if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) vty_out(vty, " BGP vpn label event debugging is on\n"); + if (BGP_DEBUG(flowspec, FLOWSPEC)) + vty_out(vty, " BGP flowspec debugging is on\n"); vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1811,6 +1817,8 @@ int bgp_debug_count(void) ret++; if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) ret++; + if (BGP_DEBUG(flowspec, FLOWSPEC)) + ret++; return ret; } @@ -1904,6 +1912,10 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp vpn label\n"); write++; } + if (CONF_BGP_DEBUG(flowspec, FLOWSPEC)) { + vty_out(vty, "debug bgp flowspec\n"); + write++; + } return write; } @@ -2204,7 +2216,17 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), prefix2str(pu, pfx_buf, sizeof(pfx_buf)), tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); - else + else if (safi == SAFI_FLOWSPEC) { + char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; + const struct prefix_fs *fs = pu.fs; + + bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr, + fs->prefix.prefixlen, + return_string, + NLRI_STRING_FORMAT_DEBUG, NULL); + snprintf(str, size, "FS %s Match{%s}", afi2str(afi), + return_string); + } else snprintf(str, size, "%s%s%s %s %s", prefix2str(pu, pfx_buf, sizeof(pfx_buf)), tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index d5dee59910..a0b179e213 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -73,6 +73,7 @@ extern unsigned long conf_bgp_debug_allow_martians; extern unsigned long conf_bgp_debug_nht; extern unsigned long conf_bgp_debug_update_groups; extern unsigned long conf_bgp_debug_vpn; +extern unsigned long conf_bgp_debug_flowspec; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -85,6 +86,7 @@ extern unsigned long term_bgp_debug_allow_martians; extern unsigned long term_bgp_debug_nht; extern unsigned long term_bgp_debug_update_groups; extern unsigned long term_bgp_debug_vpn; +extern unsigned long term_bgp_debug_flowspec; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -117,6 +119,7 @@ struct bgp_debug_filter { #define BGP_DEBUG_VPN_LEAK_TO_VRF 0x02 #define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04 #define BGP_DEBUG_VPN_LEAK_LABEL 0x08 +#define BGP_DEBUG_FLOWSPEC 0x01 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 8b60ead383..54ec7d392b 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -33,6 +33,13 @@ #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_flowspec_private.h" + +/* struct used to dump the rate contained in FS set traffic-rate EC */ +union traffic_rate { + float rate_float; + uint8_t rate_byte[4]; +}; /* Hash of community attribute. */ static struct hash *ecomhash; @@ -661,8 +668,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } /* Space between each value. */ - if (!first) + if (!first) { str_buf[str_pnt++] = ' '; + len++; + } pnt = ecom->val + (i * 8); @@ -727,6 +736,61 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) "MM:%u", seqnum); } else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP) { + sub_type = *pnt++; + + if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { + char action[64]; + char *ptr = action; + + if (*(pnt+3) == + 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL) + ptr += snprintf(ptr, sizeof(action), + "terminate (apply)"); + else + ptr += snprintf(ptr, sizeof(action), + "eval stops"); + if (*(pnt+3) == + 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) + snprintf(ptr, sizeof(action) - + (size_t)(ptr-action), + ", sample"); + len = snprintf(str_buf + str_pnt, + str_size - len, + "FS:action %s", action); + } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) { + union traffic_rate data; + + data.rate_byte[3] = *(pnt+2); + data.rate_byte[2] = *(pnt+3); + data.rate_byte[1] = *(pnt+4); + data.rate_byte[0] = *(pnt+5); + len = sprintf( + str_buf + str_pnt, + "FS:rate %f", data.rate_float); + } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { + char buf[16]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str(buf, (uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); + len = snprintf( + str_buf + str_pnt, + str_size - len, + "FS:redirect VRF %s", buf); + } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) { + len = sprintf( + str_buf + str_pnt, + "FS:marking %u", *(pnt+5)); + } else if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { + len = sprintf( + str_buf + str_pnt, + "FS:redirect IP 0x%x", *(pnt+5)); + } else + unk_ecom = 1; } else unk_ecom = 1; diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 4cdb8b8ac8..31ff1481ba 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -27,10 +27,19 @@ #define ECOMMUNITY_ENCODE_AS4 0x02 #define ECOMMUNITY_ENCODE_OPAQUE 0x03 #define ECOMMUNITY_ENCODE_EVPN 0x06 +#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 /* Flow Spec */ +/* RFC7674 */ +#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81 +#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82 /* Low-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ROUTE_TARGET 0x02 #define ECOMMUNITY_SITE_ORIGIN 0x03 +#define ECOMMUNITY_TRAFFIC_RATE 0x06 /* Flow Spec */ +#define ECOMMUNITY_TRAFFIC_ACTION 0x07 +#define ECOMMUNITY_REDIRECT_VRF 0x08 +#define ECOMMUNITY_TRAFFIC_MARKING 0x09 +#define ECOMMUNITY_REDIRECT_IP_NH 0x00 /* Low-order octet of the Extended Communities type field for EVPN types */ #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00 @@ -53,7 +62,7 @@ #define ECOMMUNITY_SIZE 8 /* Extended Communities type flag. */ -#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 +#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 /* Extended Communities attribute. */ struct ecommunity { diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c new file mode 100644 index 0000000000..5db7e37089 --- /dev/null +++ b/bgpd/bgp_flowspec.c @@ -0,0 +1,195 @@ +/* BGP FlowSpec for packet handling + * Portions: + * Copyright (C) 2017 ChinaTelecom SDN Group + * 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 "math.h" + +#include +#include "prefix.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_flowspec.h" +#include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_flowspec_private.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_debug.h" + +static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) +{ + uint32_t offset = 0; + int type; + int ret = 0, error = 0; + + while (offset < len-1) { + type = nlri_content[offset]; + offset++; + switch (type) { + case FLOWSPEC_DEST_PREFIX: + case FLOWSPEC_SRC_PREFIX: + ret = bgp_flowspec_ip_address( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + 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_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + break; + case FLOWSPEC_PKT_LEN: + case FLOWSPEC_DSCP: + ret = bgp_flowspec_op_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + break; + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_fragment_type_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); + break; + default: + error = -1; + break; + } + offset += ret; + if (error < 0) + break; + } + return error; +} + +int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, + struct bgp_nlri *packet, int withdraw) +{ + uint8_t *pnt; + uint8_t *lim; + afi_t afi; + safi_t safi; + int psize = 0; + uint8_t rlen; + struct prefix p; + int ret; + void *temp; + + /* Start processing the NLRI - there may be multiple in the MP_REACH */ + pnt = packet->nlri; + lim = pnt + packet->length; + afi = packet->afi; + safi = packet->safi; + + if (afi == AFI_IP6) { + zlog_err("BGP flowspec IPv6 not supported"); + return -1; + } + + if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT) { + zlog_err("BGP flowspec nlri length maximum reached (%u)", + packet->length); + return -1; + } + + for (; pnt < lim; pnt += psize) { + /* Clear prefix structure. */ + memset(&p, 0, sizeof(struct prefix)); + + /* All FlowSpec NLRI begin with length. */ + if (pnt + 1 > lim) + return -1; + + psize = rlen = *pnt++; + + /* When packet overflow occur return immediately. */ + if (pnt + psize > lim) { + zlog_err("Flowspec NLRI length inconsistent ( size %u seen)", + psize); + return -1; + } + if (bgp_fs_nlri_validate(pnt, psize) < 0) { + zlog_err("Bad flowspec format or NLRI options not supported"); + return -1; + } + p.family = AF_FLOWSPEC; + p.prefixlen = 0; + /* Flowspec encoding is in bytes */ + p.u.prefix_flowspec.prefixlen = psize; + temp = XCALLOC(MTYPE_TMP, psize); + memcpy(temp, pnt, psize); + p.u.prefix_flowspec.ptr = (uintptr_t) temp; + + if (BGP_DEBUG(flowspec, FLOWSPEC)) { + char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; + char local_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; + char ec_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; + char *s = NULL; + + bgp_fs_nlri_get_string((unsigned char *) + p.u.prefix_flowspec.ptr, + p.u.prefix_flowspec.prefixlen, + return_string, + NLRI_STRING_FORMAT_MIN, NULL); + snprintf(ec_string, BGP_FLOWSPEC_NLRI_STRING_MAX, + "EC{none}"); + if (attr && attr->ecommunity) { + s = ecommunity_ecom2str(attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + snprintf(ec_string, + BGP_FLOWSPEC_NLRI_STRING_MAX, + "EC{%s}", + s == NULL ? "none" : s); + } + snprintf(local_string, BGP_FLOWSPEC_NLRI_STRING_MAX, + "FS Rx %s %s %s %s", withdraw ? + "Withdraw":"Update", + afi2str(afi), return_string, + attr != NULL ? ec_string : ""); + zlog_info("%s", local_string); + } + /* Process the route. */ + if (!withdraw) + ret = bgp_update(peer, &p, 0, attr, + afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + NULL, NULL, 0, 0, NULL); + else + ret = bgp_withdraw(peer, &p, 0, attr, + afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + NULL, NULL, 0, NULL); + if (ret) { + zlog_err("Flowspec NLRI failed to be %s.", + attr ? "added" : "withdrawn"); + return -1; + } + } + return 0; +} diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h new file mode 100644 index 0000000000..392b321530 --- /dev/null +++ b/bgpd/bgp_flowspec.h @@ -0,0 +1,50 @@ +/* BGP Flowspec header for packet handling + * 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 + */ + +#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 NLRI_STRING_FORMAT_JSON 3 +#define NLRI_STRING_FORMAT_JSON_SIMPLE 4 + +#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, + json_object *json_path); + +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 new file mode 100644 index 0000000000..dede4e03d3 --- /dev/null +++ b/bgpd/bgp_flowspec_private.h @@ -0,0 +1,44 @@ +/* BGP Flowspec header . private structs and defines + * 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 + */ + +#ifndef _FRR_BGP_FLOWSPEC_PRIVATE_H +#define _FRR_BGP_FLOWSPEC_PRIVATE_H + +#define FLOWSPEC_NLRI_SIZELIMIT 240 + +/* Flowspec raffic action bit*/ +#define FLOWSPEC_TRAFFIC_ACTION_TERMINAL 1 +#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_util.c b/bgpd/bgp_flowspec_util.c new file mode 100644 index 0000000000..007b27f17e --- /dev/null +++ b/bgpd/bgp_flowspec_util.c @@ -0,0 +1,458 @@ +/* BGP FlowSpec Utilities + * Portions: + * Copyright (C) 2017 ChinaTelecom SDN Group + * 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 "zebra.h" + +#include "prefix.h" + +#include "bgp_table.h" +#include "bgp_flowspec_util.h" +#include "bgp_flowspec_private.h" + +static void hex2bin(uint8_t *hex, int *bin) +{ + int remainder = *hex; + int i = 0; + + while (remainder >= 1 && i < 8) { + bin[7-i] = remainder % 2; + remainder = remainder / 2; + i++; + } + for (; i < 8; i++) + bin[7-i] = 0; +} + +static int hexstr2num(uint8_t *hexstr, int len) +{ + int i = 0; + int num = 0; + + for (i = 0; i < len; i++) + num = hexstr[i] + 16*16*num; + return num; +} + + +/* + * handle the flowspec address src/dst or generic address NLRI + * return number of bytes analysed ( >= 0). + */ +int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error) +{ + char *display = (char *)result; /* for return_string */ + struct prefix *prefix = (struct prefix *)result; + uint32_t offset = 0; + struct prefix prefix_local; + int psize; + + *error = 0; + memset(&prefix_local, 0, sizeof(struct prefix)); + /* read the prefix length */ + prefix_local.prefixlen = nlri_ptr[offset]; + psize = PSIZE(prefix_local.prefixlen); + offset++; + /* TODO Flowspec IPv6 Support */ + prefix_local.family = AF_INET; + /* Prefix length check. */ + if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) + *error = -1; + /* When packet overflow occur return immediately. */ + if (psize + offset > max_len) + *error = -1; + /* Defensive coding, double-check + * the psize fits in a struct prefix + */ + if (psize > (ssize_t)sizeof(prefix_local.u)) + *error = -1; + memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize); + offset += psize; + switch (type) { + case BGP_FLOWSPEC_RETURN_STRING: + prefix2str(&prefix_local, display, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); + break; + case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: + PREFIX_COPY_IPV4(prefix, &prefix_local) + break; + case BGP_FLOWSPEC_VALIDATE_ONLY: + default: + break; + } + return offset; +} + +/* + * handle the flowspec operator NLRI + * return number of bytes analysed + * if there is an error, the passed error param is used to give error: + * -1 if decoding error, + * if result is a string, its assumed length + * is BGP_FLOWSPEC_STRING_DISPLAY_MAX + */ +int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error) +{ + int op[8]; + int len, value, value_size; + int loop = 0; + char *ptr = (char *)result; /* for return_string */ + uint32_t offset = 0; + int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; + int len_written; + + *error = 0; + do { + hex2bin(&nlri_ptr[offset], op); + offset++; + len = 2*op[2]+op[3]; + value_size = 1 << len; + value = hexstr2num(&nlri_ptr[offset], value_size); + /* can not be < and > at the same time */ + if (op[5] == 1 && op[6] == 1) + *error = -1; + /* if first element, AND bit can not be set */ + if (op[1] == 1 && loop == 0) + *error = -1; + switch (type) { + case BGP_FLOWSPEC_RETURN_STRING: + if (loop) { + len_written = snprintf(ptr, len_string, + ", "); + len_string -= len_written; + ptr += len_written; + } + if (op[5] == 1) { + len_written = snprintf(ptr, len_string, + "<"); + len_string -= len_written; + ptr += len_written; + } + if (op[6] == 1) { + len_written = snprintf(ptr, len_string, + ">"); + len_string -= len_written; + ptr += len_written; + } + if (op[7] == 1) { + len_written = snprintf(ptr, len_string, + "="); + len_string -= len_written; + ptr += len_written; + } + len_written = snprintf(ptr, len_string, + " %d ", value); + len_string -= len_written; + ptr += len_written; + break; + case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: + /* TODO : FS OPAQUE */ + break; + case BGP_FLOWSPEC_VALIDATE_ONLY: + default: + /* no action */ + break; + } + offset += value_size; + loop++; + } while (op[0] == 0 && offset < max_len - 1); + if (offset > max_len) + *error = -1; + /* use error parameter to count the number of entries */ + if (*error == 0) + *error = loop; + return offset; +} + + +/* + * handle the flowspec tcpflags field + * return number of bytes analysed + * if there is an error, the passed error param is used to give error: + * -1 if decoding error, + * if result is a string, its assumed length + * is BGP_FLOWSPEC_STRING_DISPLAY_MAX + */ +int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error) +{ + int op[8]; + int len, value_size, loop = 0, value; + char *ptr = (char *)result; /* for return_string */ + uint32_t offset = 0; + int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; + int len_written; + + *error = 0; + do { + hex2bin(&nlri_ptr[offset], op); + /* if first element, AND bit can not be set */ + if (op[1] == 1 && loop == 0) + *error = -1; + offset++; + len = 2 * op[2] + op[3]; + value_size = 1 << len; + value = hexstr2num(&nlri_ptr[offset], value_size); + switch (type) { + case BGP_FLOWSPEC_RETURN_STRING: + if (op[1] == 1 && loop != 0) { + len_written = snprintf(ptr, len_string, + ", and "); + len_string -= len_written; + ptr += len_written; + } else if (op[1] == 0 && loop != 0) { + len_written = snprintf(ptr, len_string, + ", or "); + len_string -= len_written; + ptr += len_written; + } + len_written = snprintf(ptr, len_string, + "tcp flags is "); + len_string -= len_written; + ptr += len_written; + if (op[6] == 1) { + ptr += snprintf(ptr, len_string, + "not "); + len_string -= len_written; + ptr += len_written; + } + if (op[7] == 1) { + ptr += snprintf(ptr, len_string, + "exactly match "); + len_string -= len_written; + ptr += len_written; + } + ptr += snprintf(ptr, len_string, + "%d", value); + len_string -= len_written; + ptr += len_written; + break; + case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: + /* TODO : FS OPAQUE */ + break; + case BGP_FLOWSPEC_VALIDATE_ONLY: + default: + /* no action */ + break; + } + offset += value_size; + loop++; + } while (op[0] == 0 && offset < max_len - 1); + if (offset > max_len) + *error = -1; + /* use error parameter to count the number of entries */ + if (*error == 0) + *error = loop; + return offset; +} + +/* + * handle the flowspec fragment type field + * return error (returned values are invalid) or number of bytes analysed + * -1 if error in decoding + * >= 0 : number of bytes analysed (ok). + */ +int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error) +{ + int op[8]; + int len, value, value_size, loop = 0; + char *ptr = (char *)result; /* for return_string */ + uint32_t offset = 0; + int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; + int len_written; + + *error = 0; + do { + hex2bin(&nlri_ptr[offset], op); + offset++; + len = 2 * op[2] + op[3]; + value_size = 1 << len; + value = hexstr2num(&nlri_ptr[offset], value_size); + if (value != 1 && value != 2 && value != 4 && value != 8) + *error = -1; + offset += value_size; + /* TODO : as per RFC5574 : first Fragment bits are Reserved + * does that mean that it is not possible + * to handle multiple occurences ? + * as of today, we only grab the first TCP fragment + */ + if (loop) { + *error = -2; + loop++; + continue; + } + switch (type) { + case BGP_FLOWSPEC_RETURN_STRING: + switch (value) { + case 1: + len_written = snprintf(ptr, len_string, + "dont-fragment"); + len_string -= len_written; + ptr += len_written; + break; + case 2: + len_written = snprintf(ptr, len_string, + "is-fragment"); + len_string -= len_written; + ptr += len_written; + break; + case 4: + len_written = snprintf(ptr, len_string, + "first-fragment"); + len_string -= len_written; + ptr += len_written; + break; + case 8: + len_written = snprintf(ptr, len_string, + "last-fragment"); + len_string -= len_written; + ptr += len_written; + break; + default: + {} + } + break; + case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: + /* TODO : FS OPAQUE */ + break; + case BGP_FLOWSPEC_VALIDATE_ONLY: + default: + /* no action */ + break; + } + loop++; + } while (op[0] == 0 && offset < max_len - 1); + if (offset > max_len) + *error = -1; + return offset; +} + + +static bool bgp_flowspec_contains_prefix(struct prefix *pfs, + struct prefix *input, + int prefix_check) +{ + uint32_t offset = 0; + int type; + int ret = 0, error = 0; + uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; + size_t len = pfs->u.prefix_flowspec.prefixlen; + struct prefix compare; + + error = 0; + while (offset < len-1 && error >= 0) { + type = nlri_content[offset]; + offset++; + switch (type) { + case FLOWSPEC_DEST_PREFIX: + case FLOWSPEC_SRC_PREFIX: + memset(&compare, 0, sizeof(struct prefix)); + ret = bgp_flowspec_ip_address( + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, + nlri_content+offset, + len - offset, + &compare, &error); + if (ret <= 0) + break; + if (prefix_check && + compare.prefixlen != input->prefixlen) + break; + if (compare.family != input->family) + break; + if ((input->family == AF_INET) && + IPV4_ADDR_SAME(&input->u.prefix4, + &compare.u.prefix4)) + return true; + if ((input->family == AF_INET6) && + IPV6_ADDR_SAME(&input->u.prefix6.s6_addr, + &compare.u.prefix6.s6_addr)) + return true; + 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_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; + case FLOWSPEC_PKT_LEN: + case FLOWSPEC_DSCP: + ret = bgp_flowspec_op_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, + &error); + break; + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_fragment_type_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, + &error); + break; + default: + error = -1; + break; + } + offset += ret; + } + return false; +} + +struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi, + struct bgp_table *rib, + struct prefix *match, + int prefix_check) +{ + struct bgp_node *rn; + struct prefix *prefix; + + for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { + prefix = &rn->p; + + if (prefix->family != AF_FLOWSPEC) + continue; + + if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) + return rn; + } + return NULL; +} diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h new file mode 100644 index 0000000000..aa21461102 --- /dev/null +++ b/bgpd/bgp_flowspec_util.h @@ -0,0 +1,58 @@ +/* BGP Flowspec header for utilities + * 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 + */ + +#ifndef _FRR_BGP_FLOWSPEC_UTIL_H +#define _FRR_BGP_FLOWSPEC_UTIL_H + +#include "zclient.h" + +#define BGP_FLOWSPEC_STRING_DISPLAY_MAX 512 + +enum bgp_flowspec_util_nlri_t { + BGP_FLOWSPEC_VALIDATE_ONLY = 0, + BGP_FLOWSPEC_RETURN_STRING = 1, + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE = 2, + BGP_FLOWSPEC_RETURN_JSON = 3, +}; + + +extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error); + +extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error); + +extern int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error); + +extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, + uint8_t *nlri_ptr, + uint32_t max_len, + void *result, int *error); + +extern struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi, + struct bgp_table *rib, + struct prefix *match, + int prefix_check); +#endif /* _FRR_BGP_FLOWSPEC_UTIL_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c new file mode 100644 index 0000000000..247da5d183 --- /dev/null +++ b/bgpd/bgp_flowspec_vty.c @@ -0,0 +1,414 @@ +/* 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" +#include "bgpd/bgp_debug.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, remaining_len) do { \ + int _len_written; \ + \ + if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) {\ + _len_written = snprintf((ptr), (remaining_len), \ + ", "); \ + (remaining_len) -= _len_written; \ + (ptr) += _len_written; \ + } else if (((format) == NLRI_STRING_FORMAT_MIN) \ + && (count)) { \ + _len_written = snprintf((ptr), (remaining_len), \ + " "); \ + (remaining_len) -= _len_written; \ + (ptr) += _len_written; \ + } \ + count++; \ + } while (0) + +/* Parse FLOWSPEC NLRI + * passed return_string string has assumed length + * BGP_FLOWSPEC_STRING_DISPLAY_MAX + */ +void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, + char *return_string, int format, + json_object *json_path) +{ + 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; + enum bgp_flowspec_util_nlri_t type_util; + int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; + int len_written; + + 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; + /* if needed. type_util can be set to other values */ + type_util = BGP_FLOWSPEC_RETURN_STRING; + 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( + type_util, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + if (json_path) { + json_object_string_add(json_path, + lookup_msg(bgp_flowspec_display, type, ""), + local_string); + break; + } + FS_STRING_UPDATE(count, ptr, format, len_string); + len_written = snprintf(ptr, len_string, "%s%s %s%s", + pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + len_string -= len_written; + ptr += len_written; + 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(type_util, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + if (json_path) { + json_object_string_add(json_path, + lookup_msg(bgp_flowspec_display, type, ""), + local_string); + break; + } + FS_STRING_UPDATE(count, ptr, format, len_string); + len_written = snprintf(ptr, len_string, "%s%s %s%s", + pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + len_string -= len_written; + ptr += len_written; + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + type_util, + nlri_content+offset, + len - offset, + local_string, &error); + if (ret <= 0) + break; + if (json_path) { + json_object_string_add(json_path, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string); + break; + } + FS_STRING_UPDATE(count, ptr, format, len_string); + len_written = snprintf(ptr, len_string, "%s%s %s%s", + pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + len_string -= len_written; + ptr += len_written; + break; + case FLOWSPEC_PKT_LEN: + case FLOWSPEC_DSCP: + ret = bgp_flowspec_op_decode( + type_util, + nlri_content + offset, + len - offset, local_string, + &error); + if (ret <= 0) + break; + if (json_path) { + json_object_string_add(json_path, + lookup_msg(bgp_flowspec_display, type, ""), + local_string); + break; + } + FS_STRING_UPDATE(count, ptr, format, len_string); + len_written = snprintf(ptr, len_string, "%s%s %s%s", + pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + len_string -= len_written; + ptr += len_written; + break; + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_fragment_type_decode( + type_util, + nlri_content + offset, + len - offset, local_string, + &error); + if (ret <= 0) + break; + if (json_path) { + json_object_string_add(json_path, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string); + break; + } + FS_STRING_UPDATE(count, ptr, format, len_string); + len_written = snprintf(ptr, len_string, "%s%s %s%s", + pre_extra, + lookup_msg(bgp_flowspec_display, + type, ""), + local_string, extra); + len_string -= len_written; + ptr += len_written; + 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; + json_object *json_nlri_path = NULL; + json_object *json_ecom_path = NULL; + json_object *json_time_path = NULL; + char timebuf[BGP_UPTIME_LEN]; + + /* Print prefix */ + if (p != NULL) { + if (p->family != AF_FLOWSPEC) + return; + if (json_paths) { + if (display == NLRI_STRING_FORMAT_JSON) + json_nlri_path = json_object_new_object(); + else + json_nlri_path = json_paths; + } + 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, + json_nlri_path); + 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 if (display == NLRI_STRING_FORMAT_MIN) + vty_out(vty, " %-30s", return_string); + else if (json_paths && display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, json_nlri_path); + } + 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 if (display == NLRI_STRING_FORMAT_MIN) + vty_out(vty, "%s", s); + else if (json_paths) { + json_ecom_path = json_object_new_object(); + json_object_string_add(json_ecom_path, + "ecomlist", s); + if (display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, + json_ecom_path); + } + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "\tup for %8s\n", timebuf); + else if (json_paths) { + json_time_path = json_object_new_object(); + json_object_string_add(json_time_path, + "time", timebuf); + if (display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, json_time_path); + } + +} + +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 = NLRI_STRING_FORMAT_LARGE; + + if (type != bgp_show_type_detail) + return CMD_SUCCESS; + + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + if (rn->info == NULL) + continue; + if (use_json) { + json_paths = json_object_new_array(); + display = NLRI_STRING_FORMAT_JSON; + } + for (ri = rn->info; ri; ri = ri->next) { + total_count++; + route_vty_out_flowspec(vty, &rn->p, + ri, display, + json_paths); + + } + if (use_json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_paths, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json_paths); + json_paths = NULL; + } + } + if (total_count && !use_json) + vty_out(vty, + "\nDisplayed %ld flowspec entries\n", + total_count); + return CMD_SUCCESS; +} + +DEFUN (debug_bgp_flowspec, + debug_bgp_flowspec_cmd, + "debug bgp flowspec", + DEBUG_STR + BGP_STR + "BGP allow flowspec debugging entries\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON(flowspec, FLOWSPEC); + else { + TERM_DEBUG_ON(flowspec, FLOWSPEC); + vty_out(vty, "BGP flowspec debugging is on\n"); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_flowspec, + no_debug_bgp_flowspec_cmd, + "no debug bgp flowspec", + NO_STR + DEBUG_STR + BGP_STR + "BGP allow flowspec debugging entries\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF(flowspec, FLOWSPEC); + else { + TERM_DEBUG_OFF(flowspec, FLOWSPEC); + vty_out(vty, "BGP flowspec debugging is off\n"); + } + return CMD_SUCCESS; +} + +void bgp_flowspec_vty_init(void) +{ + install_element(ENABLE_NODE, &debug_bgp_flowspec_cmd); + install_element(CONFIG_NODE, &debug_bgp_flowspec_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); +} diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 64543ff019..4669fad3b7 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -119,3 +119,10 @@ DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP") + +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC, "BGP flowspec") +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_RULE, "BGP flowspec rule") +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_RULE_STR, "BGP flowspec rule str") +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_COMPILED, "BGP flowspec compiled") +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_NAME, "BGP flowspec name") +DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_INDEX, "BGP flowspec index") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index fae98329c6..6fa3040a19 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -115,4 +115,12 @@ DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_MACIP) + +DECLARE_MTYPE(BGP_FLOWSPEC) +DECLARE_MTYPE(BGP_FLOWSPEC_RULE) +DECLARE_MTYPE(BGP_FLOWSPEC_RULE_STR) +DECLARE_MTYPE(BGP_FLOWSPEC_COMPILED) +DECLARE_MTYPE(BGP_FLOWSPEC_NAME) +DECLARE_MTYPE(BGP_FLOWSPEC_INDEX) + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 5ec63458f5..aa98f8a557 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -146,6 +146,12 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, "capabilityErrorMultiProtocolSafi", "EVPN"); break; + case SAFI_FLOWSPEC: + json_object_string_add( + json_cap, + "capabilityErrorMultiProtocolSafi", + "flowspec"); + break; default: json_object_int_add( json_cap, @@ -187,6 +193,9 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, case SAFI_ENCAP: vty_out(vty, "SAFI ENCAP"); break; + case SAFI_FLOWSPEC: + vty_out(vty, "SAFI FLOWSPEC"); + break; case SAFI_EVPN: vty_out(vty, "SAFI EVPN"); break; @@ -1166,11 +1175,13 @@ int bgp_open_option_parse(struct peer *peer, uint8_t length, int *mp_capability) && !peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] && !peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] && !peer->afc_nego[AFI_IP][SAFI_ENCAP] + && !peer->afc_nego[AFI_IP][SAFI_FLOWSPEC] && !peer->afc_nego[AFI_IP6][SAFI_UNICAST] && !peer->afc_nego[AFI_IP6][SAFI_MULTICAST] && !peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] && !peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] && !peer->afc_nego[AFI_IP6][SAFI_ENCAP] + && !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) { zlog_err( "%s [Error] Configured AFI/SAFIs do not " diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 3c7bb65fd3..f0b30f0186 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -59,6 +59,7 @@ #include "bgpd/bgp_label.h" #include "bgpd/bgp_io.h" #include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_flowspec.h" /** * Sets marker and type fields for a BGP message. @@ -302,6 +303,8 @@ int bgp_nlri_parse(struct peer *peer, struct attr *attr, packet); case SAFI_EVPN: return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw); + case SAFI_FLOWSPEC: + return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw); } return -1; } @@ -1275,6 +1278,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) peer->afc[AFI_IP][SAFI_MULTICAST]; peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] = peer->afc[AFI_IP][SAFI_LABELED_UNICAST]; + peer->afc_nego[AFI_IP][SAFI_FLOWSPEC] = + peer->afc[AFI_IP][SAFI_FLOWSPEC]; peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = @@ -1283,6 +1288,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]; peer->afc_nego[AFI_L2VPN][SAFI_EVPN] = peer->afc[AFI_L2VPN][SAFI_EVPN]; + peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] = + peer->afc[AFI_IP6][SAFI_FLOWSPEC]; } /* When collision is detected and this peer is closed. Retrun diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a37e709f10..e4769fe1d6 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -73,6 +73,8 @@ #include "bgpd/bgp_encap_tlv.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_vty.h" +#include "bgpd/bgp_flowspec.h" +#include "bgpd/bgp_flowspec_util.h" #ifndef VTYSH_EXTRACT_PL #include "bgpd/bgp_route_clippy.c" @@ -5551,7 +5553,8 @@ void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p, /* MPLS-VPN aggregation is not yet supported. */ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) - || (safi == SAFI_EVPN)) + || (safi == SAFI_EVPN) + || (safi == SAFI_FLOWSPEC)) return; table = bgp->aggregate[afi][safi]; @@ -5589,7 +5592,8 @@ void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p, /* MPLS-VPN aggregation is not yet supported. */ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) - || (safi == SAFI_EVPN)) + || (safi == SAFI_EVPN) + || (safi == SAFI_FLOWSPEC)) return; table = bgp->aggregate[afi][safi]; @@ -5817,6 +5821,9 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, struct bgp_node *rn; struct bgp_aggregate *aggregate; + if (safi == SAFI_FLOWSPEC) + return CMD_WARNING_CONFIG_FAILED; + /* Convert string to prefix structure. */ ret = str2prefix(prefix_str, &p); if (!ret) { @@ -5860,6 +5867,9 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, struct bgp_node *rn; struct bgp_aggregate *aggregate; + if (safi == SAFI_FLOWSPEC) + return CMD_WARNING_CONFIG_FAILED; + /* Convert string to prefix structure. */ ret = str2prefix(prefix_str, &p); if (!ret) { @@ -6308,6 +6318,11 @@ 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, + json ? + NLRI_STRING_FORMAT_JSON_SIMPLE : + NLRI_STRING_FORMAT_MIN, json); } else { if (!json) len = vty_out( @@ -6499,9 +6514,10 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, "used"); } else vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); - } + } else if (safi == SAFI_FLOWSPEC) { + /* already done */ /* IPv4 Next Hop */ - else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { + } else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { if (json_paths) { json_nexthop_global = json_object_new_object(); @@ -8421,6 +8437,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; @@ -8703,6 +8725,18 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bgp_unlock_node(rm); } + } else if (safi == SAFI_FLOWSPEC) { + rn = bgp_flowspec_get_match_per_ip(afi, rib, + &match, prefix_check); + if (rn != NULL) { + route_vty_out_flowspec(vty, &rn->p, + rn->info, use_json ? + NLRI_STRING_FORMAT_JSON : + NLRI_STRING_FORMAT_LARGE, + json_paths); + display++; + bgp_unlock_node(rn); + } } else { header = 1; @@ -10417,6 +10451,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"]] " @@ -11431,6 +11491,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, }; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 475a8ea746..a3c7994b1b 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -80,6 +80,8 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi) case SAFI_MPLS_VPN: return BGP_VPNV4_NODE; break; + case SAFI_FLOWSPEC: + return BGP_FLOWSPECV4_NODE; default: /* not expected */ return BGP_IPV4_NODE; @@ -100,6 +102,8 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi) case SAFI_MPLS_VPN: return BGP_VPNV6_NODE; break; + case SAFI_FLOWSPEC: + return BGP_FLOWSPECV6_NODE; default: /* not expected */ return BGP_IPV4_NODE; @@ -128,6 +132,7 @@ afi_t bgp_node_afi(struct vty *vty) case BGP_IPV6M_NODE: case BGP_IPV6L_NODE: case BGP_VPNV6_NODE: + case BGP_FLOWSPECV6_NODE: afi = AFI_IP6; break; case BGP_EVPN_NODE: @@ -161,6 +166,10 @@ safi_t bgp_node_safi(struct vty *vty) case BGP_IPV6L_NODE: safi = SAFI_LABELED_UNICAST; break; + case BGP_FLOWSPECV4_NODE: + case BGP_FLOWSPECV6_NODE: + safi = SAFI_FLOWSPEC; + break; default: safi = SAFI_UNICAST; break; @@ -214,6 +223,8 @@ safi_t bgp_vty_safi_from_str(const char *safi_str) safi = SAFI_MPLS_VPN; else if (strmatch(safi_str, "labeled-unicast")) safi = SAFI_LABELED_UNICAST; + else if (strmatch(safi_str, "flowspec")) + safi = SAFI_FLOWSPEC; return safi; } @@ -237,6 +248,10 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, ret = 1; if (safi) *safi = SAFI_MPLS_VPN; + } else if (argv_find(argv, argc, "flowspec", index)) { + ret = 1; + if (safi) + *safi = SAFI_FLOWSPEC; } return ret; } @@ -6645,11 +6660,11 @@ DEFPY (af_routetarget_import, } DEFUN_NOSH (address_family_ipv4_safi, - address_family_ipv4_safi_cmd, - "address-family ipv4 []", - "Enter Address Family command mode\n" - "Address Family\n" - BGP_SAFI_WITH_LABEL_HELP_STR) + address_family_ipv4_safi_cmd, + "address-family ipv4 []", + "Enter Address Family command mode\n" + "Address Family\n" + BGP_SAFI_WITH_LABEL_HELP_STR) { if (argc == 3) { @@ -6670,11 +6685,11 @@ DEFUN_NOSH (address_family_ipv4_safi, } DEFUN_NOSH (address_family_ipv6_safi, - address_family_ipv6_safi_cmd, - "address-family ipv6 []", - "Enter Address Family command mode\n" - "Address Family\n" - BGP_SAFI_WITH_LABEL_HELP_STR) + address_family_ipv6_safi_cmd, + "address-family ipv6 []", + "Enter Address Family command mode\n" + "Address Family\n" + BGP_SAFI_WITH_LABEL_HELP_STR) { if (argc == 3) { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -7943,6 +7958,8 @@ const char *afi_safi_print(afi_t afi, safi_t safi) return "IPv4 VPN"; else if (afi == AFI_IP && safi == SAFI_ENCAP) return "IPv4 Encap"; + else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) + return "IPv4 Flowspec"; else if (afi == AFI_IP6 && safi == SAFI_UNICAST) return "IPv6 Unicast"; else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) @@ -7953,6 +7970,8 @@ const char *afi_safi_print(afi_t afi, safi_t safi) return "IPv6 VPN"; else if (afi == AFI_IP6 && safi == SAFI_ENCAP) return "IPv6 Encap"; + else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) + return "IPv6 Flowspec"; else if (afi == AFI_L2VPN && safi == SAFI_EVPN) return "L2VPN EVPN"; else @@ -7977,6 +7996,8 @@ const char *afi_safi_json(afi_t afi, safi_t safi) return "ipv4Vpn"; else if (afi == AFI_IP && safi == SAFI_ENCAP) return "ipv4Encap"; + else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) + return "ipv4Flowspec"; else if (afi == AFI_IP6 && safi == SAFI_UNICAST) return "ipv6Unicast"; else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) @@ -7987,6 +8008,8 @@ const char *afi_safi_json(afi_t afi, safi_t safi) return "ipv6Vpn"; else if (afi == AFI_IP6 && safi == SAFI_ENCAP) return "ipv6Encap"; + else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) + return "ipv6Flowspec"; else if (afi == AFI_L2VPN && safi == SAFI_EVPN) return "l2VpnEvpn"; else @@ -9001,8 +9024,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, uint8_t use_json, || p->afc_recv[AFI_IP6][SAFI_MPLS_VPN] || p->afc_adv[AFI_IP6][SAFI_ENCAP] || p->afc_recv[AFI_IP6][SAFI_ENCAP] + || p->afc_adv[AFI_IP6][SAFI_FLOWSPEC] + || p->afc_recv[AFI_IP6][SAFI_FLOWSPEC] || p->afc_adv[AFI_IP][SAFI_ENCAP] || p->afc_recv[AFI_IP][SAFI_ENCAP] + || p->afc_adv[AFI_IP][SAFI_FLOWSPEC] + || p->afc_recv[AFI_IP][SAFI_FLOWSPEC] || p->afc_adv[AFI_IP][SAFI_MPLS_VPN] || p->afc_recv[AFI_IP][SAFI_MPLS_VPN]) { if (use_json) { @@ -11795,6 +11822,12 @@ static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE, static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE, "%s(config-router-af-vni)# ", 1}; +static struct cmd_node bgp_flowspecv4_node = {BGP_FLOWSPECV4_NODE, + "%s(config-router-af)# ", 1}; + +static struct cmd_node bgp_flowspecv6_node = {BGP_FLOWSPECV6_NODE, + "%s(config-router-af-vpnv6)# ", 1}; + static void community_list_vty(void); static void bgp_ac_neighbor(vector comps, struct cmd_token *token) @@ -11855,6 +11888,8 @@ void bgp_vty_init(void) install_node(&bgp_vpnv6_node, NULL); install_node(&bgp_evpn_node, NULL); install_node(&bgp_evpn_vni_node, NULL); + install_node(&bgp_flowspecv4_node, NULL); + install_node(&bgp_flowspecv6_node, NULL); /* Install default VTY commands to new nodes. */ install_default(BGP_NODE); @@ -11866,6 +11901,8 @@ void bgp_vty_init(void) install_default(BGP_IPV6L_NODE); install_default(BGP_VPNV4_NODE); install_default(BGP_VPNV6_NODE); + install_default(BGP_FLOWSPECV4_NODE); + install_default(BGP_FLOWSPECV6_NODE); install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); @@ -12098,6 +12135,8 @@ void bgp_vty_init(void) install_element(BGP_IPV6L_NODE, &neighbor_activate_cmd); install_element(BGP_VPNV4_NODE, &neighbor_activate_cmd); install_element(BGP_VPNV6_NODE, &neighbor_activate_cmd); + install_element(BGP_FLOWSPECV4_NODE, &neighbor_activate_cmd); + install_element(BGP_FLOWSPECV6_NODE, &neighbor_activate_cmd); install_element(BGP_EVPN_NODE, &neighbor_activate_cmd); /* "no neighbor activate" commands. */ @@ -12110,6 +12149,8 @@ void bgp_vty_init(void) install_element(BGP_IPV6L_NODE, &no_neighbor_activate_cmd); install_element(BGP_VPNV4_NODE, &no_neighbor_activate_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_activate_cmd); + install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_activate_cmd); + install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_activate_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_activate_cmd); /* "neighbor peer-group" set commands. */ @@ -12121,6 +12162,10 @@ void bgp_vty_init(void) install_element(BGP_IPV6L_NODE, &neighbor_set_peer_group_hidden_cmd); install_element(BGP_VPNV4_NODE, &neighbor_set_peer_group_hidden_cmd); install_element(BGP_VPNV6_NODE, &neighbor_set_peer_group_hidden_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &neighbor_set_peer_group_hidden_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &neighbor_set_peer_group_hidden_cmd); /* "no neighbor peer-group unset" commands. */ install_element(BGP_NODE, &no_neighbor_set_peer_group_cmd); @@ -12131,6 +12176,10 @@ void bgp_vty_init(void) install_element(BGP_IPV6L_NODE, &no_neighbor_set_peer_group_hidden_cmd); install_element(BGP_VPNV4_NODE, &no_neighbor_set_peer_group_hidden_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_set_peer_group_hidden_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &no_neighbor_set_peer_group_hidden_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &no_neighbor_set_peer_group_hidden_cmd); /* "neighbor softreconfiguration inbound" commands.*/ install_element(BGP_NODE, &neighbor_soft_reconfiguration_hidden_cmd); @@ -12151,6 +12200,14 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element(BGP_VPNV6_NODE, &neighbor_soft_reconfiguration_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &neighbor_soft_reconfiguration_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &no_neighbor_soft_reconfiguration_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &neighbor_soft_reconfiguration_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &no_neighbor_soft_reconfiguration_cmd); /* "neighbor attribute-unchanged" commands. */ install_element(BGP_NODE, &neighbor_attr_unchanged_hidden_cmd); @@ -12416,6 +12473,14 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_route_reflector_client_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &neighbor_route_reflector_client_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &no_neighbor_route_reflector_client_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &neighbor_route_reflector_client_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &no_neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd); @@ -12438,6 +12503,12 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd); install_element(BGP_VPNV6_NODE, &neighbor_route_server_client_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_route_server_client_cmd); + install_element(BGP_FLOWSPECV4_NODE, &neighbor_route_server_client_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &no_neighbor_route_server_client_cmd); + install_element(BGP_FLOWSPECV6_NODE, &neighbor_route_server_client_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &no_neighbor_route_server_client_cmd); /* "neighbor addpath-tx-all-paths" commands.*/ install_element(BGP_NODE, &neighbor_addpath_tx_all_paths_hidden_cmd); @@ -12665,6 +12736,10 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_prefix_list_cmd); install_element(BGP_VPNV6_NODE, &neighbor_prefix_list_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_prefix_list_cmd); + install_element(BGP_FLOWSPECV4_NODE, &neighbor_prefix_list_cmd); + install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_prefix_list_cmd); + install_element(BGP_FLOWSPECV6_NODE, &neighbor_prefix_list_cmd); + install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_prefix_list_cmd); /* "neighbor filter-list" commands. */ install_element(BGP_NODE, &neighbor_filter_list_hidden_cmd); @@ -12685,6 +12760,10 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_filter_list_cmd); install_element(BGP_VPNV6_NODE, &neighbor_filter_list_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_filter_list_cmd); + install_element(BGP_FLOWSPECV4_NODE, &neighbor_filter_list_cmd); + install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_filter_list_cmd); + install_element(BGP_FLOWSPECV6_NODE, &neighbor_filter_list_cmd); + install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_filter_list_cmd); /* "neighbor route-map" commands. */ install_element(BGP_NODE, &neighbor_route_map_hidden_cmd); @@ -12705,6 +12784,10 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_route_map_cmd); install_element(BGP_VPNV6_NODE, &neighbor_route_map_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_route_map_cmd); + install_element(BGP_FLOWSPECV4_NODE, &neighbor_route_map_cmd); + install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_route_map_cmd); + install_element(BGP_FLOWSPECV6_NODE, &neighbor_route_map_cmd); + install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_route_map_cmd); install_element(BGP_EVPN_NODE, &neighbor_route_map_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_route_map_cmd); @@ -12853,6 +12936,8 @@ void bgp_vty_init(void) install_element(BGP_IPV6L_NODE, &exit_address_family_cmd); install_element(BGP_VPNV4_NODE, &exit_address_family_cmd); install_element(BGP_VPNV6_NODE, &exit_address_family_cmd); + install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); + install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); install_element(BGP_EVPN_NODE, &exit_address_family_cmd); /* "clear ip bgp commands" */ diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 7a9546e3ef..afb85f112b 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -36,11 +36,12 @@ struct bgp; #define BGP_AFI_SAFI_CMD_STR BGP_AFI_CMD_STR" "BGP_SAFI_CMD_STR #define BGP_AFI_SAFI_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_HELP_STR -#define BGP_SAFI_WITH_LABEL_CMD_STR "" +#define BGP_SAFI_WITH_LABEL_CMD_STR "" #define BGP_SAFI_WITH_LABEL_HELP_STR \ "Address Family modifier\n" \ "Address Family modifier\n" \ "Address Family modifier\n" \ + "Address Family modifier\n" \ "Address Family modifier\n" extern void bgp_vty_init(void); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 97f0ffcf2c..ad4e7dc34c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -80,6 +80,8 @@ #include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_io.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_flowspec.h" + DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_QOBJ_TYPE(bgp_master) @@ -1626,6 +1628,8 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_ENCAP], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_FLOWSPEC], + PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_UNICAST], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_MULTICAST], @@ -1636,6 +1640,8 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_ENCAP], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_FLOWSPEC], + PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN], PEER_FLAG_REFLECTOR_CLIENT); } @@ -3641,11 +3647,13 @@ int peer_active(struct peer *peer) if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] || peer->afc[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP] + || peer->afc[AFI_IP][SAFI_FLOWSPEC] || peer->afc[AFI_IP6][SAFI_UNICAST] || peer->afc[AFI_IP6][SAFI_MULTICAST] || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP6][SAFI_MPLS_VPN] || peer->afc[AFI_IP6][SAFI_ENCAP] + || peer->afc[AFI_IP6][SAFI_FLOWSPEC] || peer->afc[AFI_L2VPN][SAFI_EVPN]) return 1; return 0; @@ -3659,11 +3667,13 @@ int peer_active_nego(struct peer *peer) || peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP][SAFI_ENCAP] + || peer->afc_nego[AFI_IP][SAFI_FLOWSPEC] || peer->afc_nego[AFI_IP6][SAFI_UNICAST] || peer->afc_nego[AFI_IP6][SAFI_MULTICAST] || peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP6][SAFI_ENCAP] + || peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] || peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) return 1; return 0; @@ -7100,6 +7110,8 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, vty_frame(vty, "ipv4 vpn"); else if (safi == SAFI_ENCAP) vty_frame(vty, "ipv4 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv4 flowspec"); } else if (afi == AFI_IP6) { if (safi == SAFI_UNICAST) vty_frame(vty, "ipv6 unicast"); @@ -7111,6 +7123,8 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, vty_frame(vty, "ipv6 vpn"); else if (safi == SAFI_ENCAP) vty_frame(vty, "ipv6 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv6 flowspec"); } else if (afi == AFI_L2VPN) { if (safi == SAFI_EVPN) vty_frame(vty, "l2vpn evpn"); @@ -7437,6 +7451,9 @@ int bgp_config_write(struct vty *vty) /* ENCAPv4 configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); + /* FLOWSPEC v4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); + /* IPv6 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); @@ -7453,6 +7470,9 @@ int bgp_config_write(struct vty *vty) /* ENCAPv6 configuration. */ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); + /* FLOWSPEC v6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); + /* EVPN configuration. */ bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); @@ -7610,6 +7630,7 @@ void bgp_init(void) rfapi_init(); #endif bgp_ethernetvpn_init(); + bgp_flowspec_vty_init(); /* Access list initialize. */ access_list_init(); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 10d6e03976..40f887b86d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -84,6 +84,8 @@ enum bgp_af_index { BGP_AF_L2VPN_EVPN, BGP_AF_IPV4_LBL_UNICAST, BGP_AF_IPV6_LBL_UNICAST, + BGP_AF_IPV4_FLOWSPEC, + BGP_AF_IPV6_FLOWSPEC, BGP_AF_MAX }; @@ -1558,6 +1560,8 @@ static inline int afindex(afi_t afi, safi_t safi) case SAFI_ENCAP: return BGP_AF_IPV4_ENCAP; break; + case SAFI_FLOWSPEC: + return BGP_AF_IPV4_FLOWSPEC; default: return BGP_AF_MAX; break; @@ -1580,6 +1584,8 @@ static inline int afindex(afi_t afi, safi_t safi) case SAFI_ENCAP: return BGP_AF_IPV6_ENCAP; break; + case SAFI_FLOWSPEC: + return BGP_AF_IPV6_FLOWSPEC; default: return BGP_AF_MAX; break; @@ -1616,6 +1622,7 @@ static inline int peer_afi_active_nego(const struct peer *peer, afi_t afi) || peer->afc_nego[afi][SAFI_LABELED_UNICAST] || peer->afc_nego[afi][SAFI_MPLS_VPN] || peer->afc_nego[afi][SAFI_ENCAP] + || peer->afc_nego[afi][SAFI_FLOWSPEC] || peer->afc_nego[afi][SAFI_EVPN]) return 1; return 0; @@ -1628,12 +1635,14 @@ static inline int peer_group_af_configured(struct peer_group *group) if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] || peer->afc[AFI_IP][SAFI_LABELED_UNICAST] + || peer->afc[AFI_IP][SAFI_FLOWSPEC] || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_UNICAST] || peer->afc[AFI_IP6][SAFI_MULTICAST] || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP6][SAFI_MPLS_VPN] || peer->afc[AFI_IP6][SAFI_ENCAP] + || peer->afc[AFI_IP6][SAFI_FLOWSPEC] || peer->afc[AFI_L2VPN][SAFI_EVPN]) return 1; return 0; diff --git a/lib/command.c b/lib/command.c index 5697c1d812..7c7fddeea5 100644 --- a/lib/command.c +++ b/lib/command.c @@ -118,6 +118,10 @@ const char *node_names[] = { "link-params", // LINK_PARAMS_NODE, "bgp evpn vni", // BGP_EVPN_VNI_NODE, "rpki", // RPKI_NODE + "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE + */ + "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE + */ }; /* Command vector which includes some level of command lists. Normally @@ -948,6 +952,8 @@ enum node_type node_parent(enum node_type node) switch (node) { case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: + case BGP_FLOWSPECV4_NODE: + case BGP_FLOWSPECV6_NODE: case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: @@ -1318,6 +1324,8 @@ void cmd_exit(struct vty *vty) case BGP_IPV4L_NODE: case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: + case BGP_FLOWSPECV4_NODE: + case BGP_FLOWSPECV6_NODE: case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: @@ -1394,6 +1402,8 @@ DEFUN (config_end, case BGP_VNC_L2_GROUP_NODE: case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: + case BGP_FLOWSPECV4_NODE: + case BGP_FLOWSPECV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV4L_NODE: diff --git a/lib/command.h b/lib/command.h index 95d8ee99df..a7fa3a1692 100644 --- a/lib/command.h +++ b/lib/command.h @@ -142,6 +142,8 @@ enum node_type { BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */ RPKI_NODE, /* RPKI node for configuration of RPKI cache server connections.*/ + BGP_FLOWSPECV4_NODE, /* BGP IPv4 FLOWSPEC Address-Family */ + BGP_FLOWSPECV6_NODE, /* BGP IPv6 FLOWSPEC Address-Family */ NODE_TYPE_MAX, /* maximum */ }; diff --git a/lib/memory.c b/lib/memory.c index 90d7d420a9..be8b100ba7 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -26,6 +26,7 @@ struct memgroup **mg_insert = &mg_first; DEFINE_MGROUP(LIB, "libfrr") DEFINE_MTYPE(LIB, TMP, "Temporary memory") +DEFINE_MTYPE(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") static inline void mt_count_alloc(struct memtype *mt, size_t size) { diff --git a/lib/memory.h b/lib/memory.h index 6de370514a..1fbbbe4231 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -127,6 +127,7 @@ struct memgroup { DECLARE_MGROUP(LIB) DECLARE_MTYPE(TMP) +DECLARE_MTYPE(PREFIX_FLOWSPEC) extern void *qmalloc(struct memtype *mt, size_t size) diff --git a/lib/prefix.c b/lib/prefix.c index 003ce992b4..ed55fac883 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -523,6 +523,8 @@ const char *safi2str(safi_t safi) return "evpn"; case SAFI_LABELED_UNICAST: return "labeled-unicast"; + case SAFI_FLOWSPEC: + return "flowspec"; default: return "unknown"; } @@ -539,6 +541,24 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (n->prefixlen > p->prefixlen) return 0; + if (n->family == AF_FLOWSPEC) { + /* prefixlen is unused. look at fs prefix len */ + if (n->u.prefix_flowspec.prefixlen > + p->u.prefix_flowspec.prefixlen) + return 0; + + /* Set both prefix's head pointer. */ + np = (const uint8_t *)&n->u.prefix_flowspec.ptr; + pp = (const uint8_t *)&p->u.prefix_flowspec.ptr; + + offset = n->u.prefix_flowspec.prefixlen; + + while (offset--) + if (np[offset] != pp[offset]) + return 0; + return 1; + } + /* Set both prefix's head pointer. */ np = (const uint8_t *)&n->u.prefix; pp = (const uint8_t *)&p->u.prefix; @@ -581,7 +601,6 @@ int prefix_match_network_statement(const struct prefix *n, return 1; } -/* Copy prefix from src to dest. */ void prefix_copy(struct prefix *dest, const struct prefix *src) { dest->family = src->family; @@ -600,6 +619,18 @@ void prefix_copy(struct prefix *dest, const struct prefix *src) } else if (src->family == AF_UNSPEC) { dest->u.lp.id = src->u.lp.id; dest->u.lp.adv_router = src->u.lp.adv_router; + } else if (src->family == AF_FLOWSPEC) { + void *temp; + int len; + + len = src->u.prefix_flowspec.prefixlen; + dest->u.prefix_flowspec.prefixlen = + src->u.prefix_flowspec.prefixlen; + dest->family = src->family; + temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); + dest->u.prefix_flowspec.ptr = (uintptr_t)temp; + memcpy((void *)dest->u.prefix_flowspec.ptr, + (void *)src->u.prefix_flowspec.ptr, len); } else { zlog_err("prefix_copy(): Unknown address family %d", src->family); @@ -639,6 +670,15 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2) if (!memcmp(&p1->u.prefix_evpn, &p2->u.prefix_evpn, sizeof(struct evpn_addr))) return 1; + if (p1->family == AF_FLOWSPEC) { + if (p1->u.prefix_flowspec.prefixlen != + p2->u.prefix_flowspec.prefixlen) + return 0; + if (!memcmp(&p1->u.prefix_flowspec.ptr, + &p2->u.prefix_flowspec.ptr, + p2->u.prefix_flowspec.prefixlen)) + return 1; + } } return 0; } @@ -659,12 +699,30 @@ int prefix_cmp(const struct prefix *p1, const struct prefix *p2) int shift; /* Set both prefix's head pointer. */ - const uint8_t *pp1 = (const uint8_t *)&p1->u.prefix; - const uint8_t *pp2 = (const uint8_t *)&p2->u.prefix; + const uint8_t *pp1; + const uint8_t *pp2; - if (p1->family != p2->family || p1->prefixlen != p2->prefixlen) + if (p1->family != p2->family) return 1; + if (p1->family == AF_FLOWSPEC) { + pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; + pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; + if (p1->u.prefix_flowspec.prefixlen != + p2->u.prefix_flowspec.prefixlen) + return 1; + + offset = p1->u.prefix_flowspec.prefixlen; + while (offset--) + if (pp1[offset] != pp2[offset]) + return 1; + return 0; + } + pp1 = (const uint8_t *)&p1->u.prefix; + pp2 = (const uint8_t *)&p2->u.prefix; + + if (p1->prefixlen != p2->prefixlen) + return 1; offset = p1->prefixlen / PNBBY; shift = p1->prefixlen % PNBBY; @@ -1207,6 +1265,10 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) prefixevpn2str(p, str, size); break; + case AF_FLOWSPEC: + sprintf(str, "FS prefix"); + break; + default: sprintf(str, "UNK prefix"); break; @@ -1386,6 +1448,24 @@ unsigned prefix_hash_key(void *pp) { struct prefix copy; + if (((struct prefix *)pp)->family == AF_FLOWSPEC) { + uint32_t len; + void *temp; + + /* make sure *all* unused bits are zero, + * particularly including alignment / + * padding and unused prefix bytes. + */ + memset(©, 0, sizeof(copy)); + prefix_copy(©, (struct prefix *)pp); + len = jhash((void *)copy.u.prefix_flowspec.ptr, + copy.u.prefix_flowspec.prefixlen, + 0x55aa5a5a); + temp = (void *)copy.u.prefix_flowspec.ptr; + XFREE(MTYPE_PREFIX_FLOWSPEC, temp); + copy.u.prefix_flowspec.ptr = (uintptr_t)NULL; + return len; + } /* make sure *all* unused bits are zero, particularly including * alignment / * padding and unused prefix bytes. */ diff --git a/lib/prefix.h b/lib/prefix.h index 133264f999..4efbc5a95c 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -106,6 +106,15 @@ struct evpn_addr { #define AF_EVPN (AF_MAX + 1) #endif +#if !defined(AF_FLOWSPEC) +#define AF_FLOWSPEC (AF_MAX + 2) +#endif + +struct flowspec_prefix { + uint16_t prefixlen; /* length in bytes */ + uintptr_t ptr; +}; + /* FRR generic prefix structure. */ struct prefix { uint8_t family; @@ -122,6 +131,7 @@ struct prefix { uint8_t val[16]; uintptr_t ptr; struct evpn_addr prefix_evpn; /* AF_EVPN */ + struct flowspec_prefix prefix_flowspec; /* AF_FLOWSPEC */ } u __attribute__((aligned(8))); }; @@ -174,6 +184,13 @@ struct prefix_ptr { uintptr_t prefix __attribute__((aligned(8))); }; +/* Prefix for a Flowspec entry */ +struct prefix_fs { + uint8_t family; + uint8_t prefixlen; /* unused */ + struct flowspec_prefix prefix __attribute__((aligned(8))); +}; + struct prefix_sg { uint8_t family; uint8_t prefixlen; @@ -191,6 +208,7 @@ union prefixptr { struct prefix_ipv4 *p4; struct prefix_ipv6 *p6; struct prefix_evpn *evp; + const struct prefix_fs *fs; } __attribute__((transparent_union)); union prefixconstptr { @@ -198,6 +216,7 @@ union prefixconstptr { const struct prefix_ipv4 *p4; const struct prefix_ipv6 *p6; const struct prefix_evpn *evp; + const struct prefix_fs *fs; } __attribute__((transparent_union)); #ifndef INET_ADDRSTRLEN diff --git a/lib/table.c b/lib/table.c index bf63609bc3..3adb793891 100644 --- a/lib/table.c +++ b/lib/table.c @@ -152,10 +152,16 @@ static void route_common(const struct prefix *n, const struct prefix *p, int i; uint8_t diff; uint8_t mask; + const uint8_t *np; + const uint8_t *pp; + uint8_t *newp; - const uint8_t *np = (const uint8_t *)&n->u.prefix; - const uint8_t *pp = (const uint8_t *)&p->u.prefix; - uint8_t *newp = (uint8_t *)&new->u.prefix; + if (n->family == AF_FLOWSPEC) + return prefix_copy(new, p); + np = (const uint8_t *)&n->u.prefix; + pp = (const uint8_t *)&p->u.prefix; + + newp = (uint8_t *)&new->u.prefix; for (i = 0; i < p->prefixlen / 8; i++) { if (np[i] == pp[i]) diff --git a/lib/zebra.h b/lib/zebra.h index 3887602231..f4f104299d 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -462,7 +462,8 @@ typedef enum { IANA_SAFI_LABELED_UNICAST = 4, IANA_SAFI_ENCAP = 7, IANA_SAFI_EVPN = 70, - IANA_SAFI_MPLS_VPN = 128 + IANA_SAFI_MPLS_VPN = 128, + IANA_SAFI_FLOWSPEC = 133 } iana_safi_t; /* Default Administrative Distance of each protocol. */ @@ -548,6 +549,8 @@ static inline safi_t safi_iana2int(iana_safi_t safi) return SAFI_EVPN; case IANA_SAFI_LABELED_UNICAST: return SAFI_LABELED_UNICAST; + case IANA_SAFI_FLOWSPEC: + return SAFI_FLOWSPEC; default: return SAFI_MAX; } @@ -568,6 +571,8 @@ static inline iana_safi_t safi_int2iana(safi_t safi) return IANA_SAFI_EVPN; case SAFI_LABELED_UNICAST: return IANA_SAFI_LABELED_UNICAST; + case SAFI_FLOWSPEC: + return IANA_SAFI_FLOWSPEC; default: return IANA_SAFI_RESERVED; } diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index fff1652473..34c35cfcc5 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -809,7 +809,26 @@ static struct test_segment { 37, SHOULD_ERR, }, - + { + .name = "IPv4", + .desc = "IPV4 MP Reach, flowspec, 1 NLRI", + .data = { + /* AFI / SAFI */ 0x0, + AFI_IP, + IANA_SAFI_FLOWSPEC, + 0x00, /* no NH */ + 0x00, + 0x06, /* FS Length */ + 0x01, /* FS dest prefix ID */ + 0x1e, /* IP */ + 0x1e, + 0x28, + 0x28, + 0x0 + }, + .len = 12, + .parses = SHOULD_PARSE, + }, {NULL, NULL, {0}, 0, 0}}; /* MP_UNREACH_NLRI tests */ @@ -906,6 +925,24 @@ static struct test_segment mp_unreach_segments[] = { (3 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)), SHOULD_PARSE, }, + { + .name = "IPv4", + .desc = "IPV4 MP Unreach, flowspec, 1 NLRI", + .data = { + /* AFI / SAFI */ 0x0, + AFI_IP, + IANA_SAFI_FLOWSPEC, + 0x06, /* FS Length */ + 0x01, /* FS dest prefix ID */ + 0x1e, /* IP */ + 0x1e, + 0x28, + 0x28, + 0x0 + }, + .len = 10, + .parses = SHOULD_PARSE, + }, {NULL, NULL, {0}, 0, 0}}; /* nlri_parse indicates 0 on successful parse, and -1 otherwise. @@ -1002,9 +1039,11 @@ int main(void) conf_bgp_debug_neighbor_events = -1UL; conf_bgp_debug_packet = -1UL; conf_bgp_debug_as4 = -1UL; + conf_bgp_debug_flowspec = -1UL; term_bgp_debug_neighbor_events = -1UL; term_bgp_debug_packet = -1UL; term_bgp_debug_as4 = -1UL; + term_bgp_debug_flowspec = -1UL; qobj_init(); cmd_init(0); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 96a5ea9e36..1e114d1ce0 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -339,6 +339,8 @@ static int vtysh_execute_func(const char *line, int pager) || saved_node == BGP_VPNV6_NODE || saved_node == BGP_IPV4_NODE || saved_node == BGP_IPV6_NODE + || saved_node == BGP_FLOWSPECV4_NODE + || saved_node == BGP_FLOWSPECV6_NODE || saved_node == BGP_IPV4M_NODE || saved_node == BGP_IPV4L_NODE || saved_node == BGP_IPV6L_NODE @@ -622,6 +624,8 @@ int vtysh_mark_file(const char *filename) || prev_node == BGP_VPNV6_NODE || prev_node == BGP_IPV4_NODE || prev_node == BGP_IPV6_NODE + || prev_node == BGP_FLOWSPECV4_NODE + || prev_node == BGP_FLOWSPECV6_NODE || prev_node == BGP_IPV4L_NODE || prev_node == BGP_IPV6L_NODE || prev_node == BGP_IPV4M_NODE @@ -992,6 +996,12 @@ static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE, static struct cmd_node bgp_vpnv6_node = {BGP_VPNV6_NODE, "%s(config-router-af)# "}; +static struct cmd_node bgp_flowspecv4_node = {BGP_FLOWSPECV4_NODE, + "%s(config-router-af)# "}; + +static struct cmd_node bgp_flowspecv6_node = {BGP_FLOWSPECV6_NODE, + "%s(config-router-af)# "}; + static struct cmd_node bgp_ipv4_node = {BGP_IPV4_NODE, "%s(config-router-af)# "}; @@ -1132,6 +1142,26 @@ DEFUNSH(VTYSH_BGPD, address_family_ipv4, address_family_ipv4_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_BGPD, address_family_flowspecv4, address_family_flowspecv4_cmd, + "address-family ipv4 flowspec", + "Enter Address Family command mode\n" + "Address Family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_FLOWSPECV4_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BGPD, address_family_flowspecv6, address_family_flowspecv6_cmd, + "address-family ipv6 flowspec", + "Enter Address Family command mode\n" + "Address Family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_FLOWSPECV6_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_BGPD, address_family_ipv4_multicast, address_family_ipv4_multicast_cmd, "address-family ipv4 multicast", "Enter Address Family command mode\n" @@ -1537,6 +1567,8 @@ static int vtysh_exit(struct vty *vty) case BGP_IPV6_NODE: case BGP_IPV6M_NODE: case BGP_IPV6L_NODE: + case BGP_FLOWSPECV4_NODE: + case BGP_FLOWSPECV6_NODE: case BGP_VRF_POLICY_NODE: case BGP_EVPN_NODE: case BGP_VNC_DEFAULTS_NODE: @@ -1591,7 +1623,9 @@ DEFUNSH(VTYSH_BGPD, exit_address_family, exit_address_family_cmd, || vty->node == BGP_IPV4L_NODE || vty->node == BGP_VPNV4_NODE || vty->node == BGP_VPNV6_NODE || vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6L_NODE || vty->node == BGP_IPV6M_NODE - || vty->node == BGP_EVPN_NODE) + || vty->node == BGP_EVPN_NODE + || vty->node == BGP_FLOWSPECV4_NODE + || vty->node == BGP_FLOWSPECV6_NODE) vty->node = BGP_NODE; return CMD_SUCCESS; } @@ -3082,6 +3116,8 @@ void vtysh_init_vty(void) install_node(&zebra_node, NULL); install_node(&bgp_vpnv4_node, NULL); install_node(&bgp_vpnv6_node, NULL); + install_node(&bgp_flowspecv4_node, NULL); + install_node(&bgp_flowspecv6_node, NULL); install_node(&bgp_ipv4_node, NULL); install_node(&bgp_ipv4m_node, NULL); install_node(&bgp_ipv4l_node, NULL); @@ -3167,6 +3203,10 @@ void vtysh_init_vty(void) install_element(BGP_VPNV4_NODE, &vtysh_quit_bgpd_cmd); install_element(BGP_VPNV6_NODE, &vtysh_exit_bgpd_cmd); install_element(BGP_VPNV6_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_quit_bgpd_cmd); install_element(BGP_IPV4_NODE, &vtysh_exit_bgpd_cmd); install_element(BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd); install_element(BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd); @@ -3226,6 +3266,8 @@ void vtysh_init_vty(void) install_element(BGP_IPV4L_NODE, &vtysh_end_all_cmd); install_element(BGP_VPNV4_NODE, &vtysh_end_all_cmd); install_element(BGP_VPNV6_NODE, &vtysh_end_all_cmd); + install_element(BGP_FLOWSPECV4_NODE, &vtysh_end_all_cmd); + install_element(BGP_FLOWSPECV6_NODE, &vtysh_end_all_cmd); install_element(BGP_IPV6_NODE, &vtysh_end_all_cmd); install_element(BGP_IPV6M_NODE, &vtysh_end_all_cmd); install_element(BGP_IPV6L_NODE, &vtysh_end_all_cmd); @@ -3299,6 +3341,8 @@ void vtysh_init_vty(void) install_element(BGP_NODE, &address_family_ipv6_vpn_cmd); install_element(BGP_NODE, &address_family_ipv6_labeled_unicast_cmd); install_element(BGP_NODE, &address_family_evpn_cmd); + install_element(BGP_NODE, &address_family_flowspecv4_cmd); + install_element(BGP_NODE, &address_family_flowspecv6_cmd); #if defined(HAVE_CUMULUS) install_element(BGP_NODE, &address_family_evpn2_cmd); #endif @@ -3311,6 +3355,8 @@ void vtysh_init_vty(void) install_element(BGP_IPV6M_NODE, &exit_address_family_cmd); install_element(BGP_EVPN_NODE, &exit_address_family_cmd); install_element(BGP_IPV6L_NODE, &exit_address_family_cmd); + install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); + install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); #if defined(HAVE_RPKI) install_element(CONFIG_NODE, &rpki_cmd);