diff options
57 files changed, 2614 insertions, 99 deletions
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 <zebra.h> +#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 <zebra.h> +#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 [<view|vrf> 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 [<view|vrf> 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 [<unicast|multicast|vpn|labeled-unicast>]", - "Enter Address Family command mode\n" - "Address Family\n" - BGP_SAFI_WITH_LABEL_HELP_STR) + address_family_ipv4_safi_cmd, + "address-family ipv4 [<unicast|multicast|vpn|labeled-unicast|flowspec>]", + "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 [<unicast|multicast|vpn|labeled-unicast>]", - "Enter Address Family command mode\n" - "Address Family\n" - BGP_SAFI_WITH_LABEL_HELP_STR) + address_family_ipv6_safi_cmd, + "address-family ipv6 [<unicast|multicast|vpn|labeled-unicast|flowspec>]", + "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 "<unicast|multicast|vpn|labeled-unicast>" +#define BGP_SAFI_WITH_LABEL_CMD_STR "<unicast|multicast|vpn|labeled-unicast|flowspec>" #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/bgp_zebra.c b/bgpd/bgp_zebra.c index bd2711f1de..e07701d42d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -970,6 +970,64 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p, return 0; } +static struct thread *bgp_tm_thread_connect; +static bool bgp_tm_status_connected; + +static int bgp_zebra_tm_connect(struct thread *t) +{ + struct zclient *zclient; + int delay = 10, ret = 0; + + zclient = THREAD_ARG(t); + if (bgp_tm_status_connected && zclient->sock > 0) + delay = 60; + else { + bgp_tm_status_connected = false; + ret = tm_table_manager_connect(zclient); + } + if (ret < 0) { + zlog_warn("Error connecting to table manager!"); + bgp_tm_status_connected = false; + } else { + if (!bgp_tm_status_connected) + zlog_debug("Connecting to table manager. Success"); + bgp_tm_status_connected = true; + } + thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, + &bgp_tm_thread_connect); + return 0; +} + +void bgp_zebra_init_tm_connect(void) +{ + int delay = 1; + + /* if already set, do nothing + */ + if (bgp_tm_thread_connect != NULL) + return; + bgp_tm_status_connected = false; + thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, + &bgp_tm_thread_connect); +} + +int bgp_zebra_get_table_range(uint32_t chunk_size, + uint32_t *start, uint32_t *end) +{ + int ret; + + if (!bgp_tm_status_connected) + return -1; + ret = tm_get_table_chunk(zclient, chunk_size, start, end); + if (ret < 0) { + zlog_err("BGP: Error getting table chunk %u", chunk_size); + return -1; + } + zlog_info("BGP: Table Manager returns range from chunk %u is [%u %u]", + chunk_size, *start, *end); + return 0; +} + void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_info *info, struct bgp *bgp, afi_t afi, safi_t safi) diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 68c495cf8b..7263317b6f 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -24,7 +24,10 @@ #include "vxlan.h" extern void bgp_zebra_init(struct thread_master *master); +extern void bgp_zebra_init_tm_connect(void); extern void bgp_zebra_destroy(void); +extern int bgp_zebra_get_table_range(uint32_t chunk_size, + uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, safi_t); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 515d90e049..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); } @@ -1973,6 +1979,10 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); } + if (safi == SAFI_FLOWSPEC) { + /* connect to table manager */ + bgp_zebra_init_tm_connect(); + } return ret; } @@ -3637,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; @@ -3655,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; @@ -7096,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"); @@ -7107,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"); @@ -7433,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); @@ -7449,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); @@ -7606,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/doc/developer/_static/overrides.css b/doc/developer/_static/overrides.css new file mode 100644 index 0000000000..1e0de66c55 --- /dev/null +++ b/doc/developer/_static/overrides.css @@ -0,0 +1,4 @@ +/* remove max-width restriction */ +div.body { + max-width: none; +} diff --git a/doc/developer/building-frr-on-alpine.rst b/doc/developer/building-frr-on-alpine.rst index 68031b3b61..6fcb5de107 100644 --- a/doc/developer/building-frr-on-alpine.rst +++ b/doc/developer/building-frr-on-alpine.rst @@ -1,4 +1,4 @@ -Building FRR dev packages on Alpine Linux from Git Source +Alpine Linux 3.7+ ========================================================= For building Alpine Linux dev packages, we use docker. diff --git a/doc/developer/conf.py b/doc/developer/conf.py index e2293b2a6b..ed91ff255f 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -188,7 +188,7 @@ html_favicon = '../figures/frr-logo-icon.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -339,3 +339,5 @@ def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI # node later on app.add_object_type('clicmd', 'clicmd') + # css overrides for HTML theme + app.add_stylesheet('overrides.css') diff --git a/doc/user/_static/overrides.css b/doc/user/_static/overrides.css new file mode 100644 index 0000000000..1e0de66c55 --- /dev/null +++ b/doc/user/_static/overrides.css @@ -0,0 +1,4 @@ +/* remove max-width restriction */ +div.body { + max-width: none; +} diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index a444822757..6504e7d206 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -604,9 +604,8 @@ Defining Peer .. index:: neighbor PEER remote-as ASN .. clicmd:: neighbor PEER remote-as ASN - Creates a new neighbor whose remote-as is ASN. PEER can be an IPv4 address - or an IPv6 address.:: + or an IPv6 address or an interface to use for the connection.:: router bgp 1 neighbor 10.0.0.1 remote-as 2 @@ -618,6 +617,19 @@ Defining Peer can't find neighbor 10.0.0.1 +.. index:: neighbor PEER remote-as internal +.. clicmd:: neighbor PEER remote-as internal + + Create a peer as you would when you specify an ASN, except that if the + peers ASN is different than mine as specified under the :clicmd:`router bgp ASN` + command the connection will be denied. + +.. index:: neighbor PEER remote-as external +.. clicmd:: neighbor PEER remote-as external + + Create a peer as you would when you specify an ASN, except that if the + peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN` + command the connection will be denied. .. _bgp-peer-commands: @@ -2076,19 +2088,9 @@ How to set up a 6-Bone connection :: - zebra configuration - =================== - ! - ! Actually there is no need to configure zebra - ! - bgpd configuration ================== ! - ! This means that routes go through zebra and into the kernel. - ! - router zebra - ! ! MP-BGP configuration ! router bgp 7675 @@ -2112,8 +2114,6 @@ How to set up a 6-Bone connection set ipv6 nexthop global 3ffe:1cfa:0:2:2c0:4fff:fe68:a225 set ipv6 nexthop local fe80::2c0:4fff:fe68:a225 ! - ! logfile FILENAME is obsolete. Please use log file FILENAME - log file bgpd.log ! diff --git a/doc/user/conf.py b/doc/user/conf.py index efe1023740..7a77e492ce 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -188,7 +188,7 @@ html_favicon = '../figures/frr-logo-icon.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] +html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -339,3 +339,5 @@ def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI # node later on app.add_object_type('clicmd', 'clicmd') + # css overrides for HTML theme + app.add_stylesheet('overrides.css') diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index acf63a6122..3e88dee905 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -198,7 +198,42 @@ Route Map Set Command .. index:: set ip next-hop IPV4_ADDRESS .. clicmd:: set ip next-hop IPV4_ADDRESS - Set the BGP nexthop address. + Set the BGP nexthop address to the specified IPV4_ADDRESS. For both + incoming and outgoing route-maps. + +.. index:: set ip next-hop peer-address +.. clicmd:: set ip next-hop peer-address + + Set the BGP nexthop address to the address of the peer. For an incoming + route-map this means the ip address of our peer is used. For an outgoing + route-map this means the ip address of our self is used to establish the + peering with our neighbor. + +.. index:: set ip next-hop unchanged +.. clicmd:: set ip next-hop unchanged + + Set the route-map as unchanged. Pass the route-map through without + changing it's value. + +.. index:: set ipv6 next-hop peer-address +.. clicmd:: set ipv6 next-hop peer-address + + Set the BGP nexthop address to the address of the peer. For an incoming + route-map this means the ipv6 address of our peer is used. For an outgoing + route-map this means the ip address of our self is used to establish the + peering with our neighbor. + +.. index:: set ipv6 next-hop prefer-global +.. clicmd:: set ipv6 next-hop prefer-global + + For Incoming and Import Route-maps if we receive a v6 global and v6 LL + address for the route, then prefer to use the global address as the nexthop. + +.. index:: set ipv6 next-hop global IPV6_ADDRESS +.. clicmd:: set ipv6 next-hop global IPV6_ADDRESS + + Set the next-hop to the specified IPV6_ADDRESS. For both incoming and + outgoing route-maps. .. index:: set local-preference LOCAL_PREF .. clicmd:: set local-preference LOCAL_PREF 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 */ }; @@ -965,6 +965,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_RULE_ADD), DESC_ENTRY(ZEBRA_RULE_DELETE), DESC_ENTRY(ZEBRA_RULE_NOTIFY_OWNER), + DESC_ENTRY(ZEBRA_TABLE_MANAGER_CONNECT), + DESC_ENTRY(ZEBRA_GET_TABLE_CHUNK), + DESC_ENTRY(ZEBRA_RELEASE_TABLE_CHUNK), }; #undef DESC_ENTRY 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/zclient.c b/lib/zclient.c index 4659bce1b1..7308beaaf2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2123,6 +2123,168 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, return 0; } +/** + * Connect to table manager in a syncronous way + * + * It first writes the request to zcient output buffer and then + * immediately reads the answer from the input buffer. + * + * @param zclient Zclient used to connect to table manager (zebra) + * @result Result of response + */ +int tm_table_manager_connect(struct zclient *zclient) +{ + int ret; + struct stream *s; + uint8_t result; + + if (zclient_debug) + zlog_debug("Connecting to Table Manager"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_TABLE_MANAGER_CONNECT, VRF_DEFAULT); + + /* proto */ + stream_putc(s, zclient->redist_default); + /* instance */ + stream_putw(s, zclient->instance); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + if (ret < 0) + return -1; + + if (zclient_debug) + zlog_debug("%s: Table manager connect request sent", + __func__); + + /* read response */ + if (zclient_read_sync_response(zclient, ZEBRA_TABLE_MANAGER_CONNECT) + != 0) + return -1; + + /* result */ + s = zclient->ibuf; + STREAM_GETC(s, result); + if (zclient_debug) + zlog_debug( + "%s: Table Manager connect response received, result %u", + __func__, result); + + return (int)result; +stream_failure: + return 0; +} + +/** + * Function to request a table chunk in a syncronous way + * + * It first writes the request to zclient output buffer and then + * immediately reads the answer from the input buffer. + * + * @param zclient Zclient used to connect to table manager (zebra) + * @param chunk_size Amount of table requested + * @param start to write first assigned chunk table RT ID to + * @param end To write last assigned chunk table RT ID to + * @result 0 on success, -1 otherwise + */ +int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, + uint32_t *start, uint32_t *end) +{ + int ret; + struct stream *s; + + if (zclient_debug) + zlog_debug("Getting Table Chunk"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_GET_TABLE_CHUNK, VRF_DEFAULT); + /* chunk size */ + stream_putl(s, chunk_size); + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = writen(zclient->sock, s->data, stream_get_endp(s)); + if (ret < 0) { + zlog_err("%s: can't write to zclient->sock", __func__); + close(zclient->sock); + zclient->sock = -1; + return -1; + } + if (ret == 0) { + zlog_err("%s: zclient->sock connection closed", __func__); + close(zclient->sock); + zclient->sock = -1; + return -1; + } + if (zclient_debug) + zlog_debug("%s: Table chunk request (%d bytes) sent", __func__, + ret); + + /* read response */ + if (zclient_read_sync_response(zclient, ZEBRA_GET_TABLE_CHUNK) != 0) + return -1; + + s = zclient->ibuf; + /* start and end table IDs */ + STREAM_GETL(s, *start); + STREAM_GETL(s, *end); + + if (zclient_debug) + zlog_debug("Table Chunk assign: %u - %u ", *start, *end); + +stream_failure: + return 0; +} + +/** + * Function to release a table chunk + * + * @param zclient Zclient used to connect to table manager (zebra) + * @param start First label of table + * @param end Last label of chunk + * @result 0 on success, -1 otherwise + */ +int tm_release_table_chunk(struct zclient *zclient, uint32_t start, + uint32_t end) +{ + struct stream *s; + + if (zclient_debug) + zlog_debug("Releasing Table Chunk"); + + if (zclient->sock < 0) + return -1; + + /* send request */ + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_RELEASE_TABLE_CHUNK, VRF_DEFAULT); + + /* start */ + stream_putl(s, start); + /* end */ + stream_putl(s, end); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + + int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) { struct stream *s; diff --git a/lib/zclient.h b/lib/zclient.h index b51c518463..04f2a5c178 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -132,6 +132,9 @@ typedef enum { ZEBRA_RULE_ADD, ZEBRA_RULE_DELETE, ZEBRA_RULE_NOTIFY_OWNER, + ZEBRA_TABLE_MANAGER_CONNECT, + ZEBRA_GET_TABLE_CHUNK, + ZEBRA_RELEASE_TABLE_CHUNK, } zebra_message_types_t; struct redist_proto { @@ -538,6 +541,12 @@ extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t *end); extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start, uint32_t end); +extern int tm_table_manager_connect(struct zclient *zclient); +extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, + uint32_t *start, uint32_t *end); +extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, + uint32_t end); + extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); extern void zebra_read_pw_status_update(int command, struct zclient *zclient, diff --git a/lib/zebra.h b/lib/zebra.h index ec530397be..f4f104299d 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -433,7 +433,8 @@ typedef enum { SAFI_ENCAP = 4, SAFI_EVPN = 5, SAFI_LABELED_UNICAST = 6, - SAFI_MAX = 7 + SAFI_FLOWSPEC = 7, + SAFI_MAX = 8 } safi_t; /* @@ -461,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. */ @@ -547,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; } @@ -567,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/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 163f2dbffd..01b8055b66 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -695,6 +695,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) int is_debug = 0; struct ospf6_inter_prefix_lsa *prefix_lsa = NULL; struct ospf6_inter_router_lsa *router_lsa = NULL; + struct ospf6_path *path; memset(&prefix, 0, sizeof(prefix)); @@ -900,6 +901,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) ospf6_route_copy_nexthops(route, abr_entry); + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, abr_entry->nh_list); + listnode_add_sort(route->paths, path); /* (7) If the routes are identical, copy the next hops over to existing route. ospf6's route table implementation will otherwise string both diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 1872c6bd36..581a899bcd 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1326,6 +1326,7 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) char buf[PREFIX2STR_BUFFER]; struct interface *ifp; int direct_connect = 0; + struct ospf6_path *path; if (OSPF6_LSA_IS_MAXAGE(lsa)) return; @@ -1417,9 +1418,14 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) ospf6_route_copy_nexthops(route, ls_entry); } + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, route->path.nh_list); + listnode_add_sort(route->paths, path); + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug(" route %s add with nh count %u", buf, + zlog_debug(" route %s add with cost %u nh count %u", + buf, route->path.cost, listcount(route->nh_list)); } @@ -1735,9 +1741,6 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) && CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)) { UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE); UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD); - zlog_debug("%s: EVENT unset REOUTE_REMOVE and ROUTE_ADD brouter %s", - __PRETTY_FUNCTION__, brouter_name); - ospf6_brouter_debug_print(brouter); } if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE)) { diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 4d436792dc..67b9b9df96 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -460,9 +460,6 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb) if (ra->type != rb->type) return (ra->type - rb->type); - if (ra->path.area_id != rb->path.area_id) - return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id)); - if (ra->path.type != rb->path.type) return (ra->path.type - rb->path.type); @@ -476,6 +473,9 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb) return (ra->path.cost - rb->path.cost); } + if (ra->path.area_id != rb->path.area_id) + return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id)); + return 0; } 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/tools/checkpatch.pl b/tools/checkpatch.pl index e66be1875f..c1fab1029a 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -5629,49 +5629,52 @@ sub process { } } +# +# Kernel macros unnused for FRR +# # Check for __attribute__ packed, prefer __packed - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { - WARN("PREFER_PACKED", - "__packed is preferred over __attribute__((packed))\n" . $herecurr); - } +# if ($realfile !~ m@\binclude/uapi/@ && +# $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { +# WARN("PREFER_PACKED", +# "__packed is preferred over __attribute__((packed))\n" . $herecurr); +# } # Check for __attribute__ aligned, prefer __aligned - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { - WARN("PREFER_ALIGNED", - "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); - } +# if ($realfile !~ m@\binclude/uapi/@ && +# $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { +# WARN("PREFER_ALIGNED", +# "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); +# } # Check for __attribute__ format(printf, prefer __printf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - if (WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; +# if ($realfile !~ m@\binclude/uapi/@ && +# $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { +# if (WARN("PREFER_PRINTF", +# "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && +# $fix) { +# $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; - } - } +# } +# } # Check for __attribute__ format(scanf, prefer __scanf - if ($realfile !~ m@\binclude/uapi/@ && - $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - if (WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; - } - } +# if ($realfile !~ m@\binclude/uapi/@ && +# $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { +# if (WARN("PREFER_SCANF", +# "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && +# $fix) { +# $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; +# } +# } # Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($^V && $^V ge 5.10.0 && - $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && - ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || - $line =~ /\b__weak\b/)) { - ERROR("WEAK_DECLARATION", - "Using weak declarations can have unintended link defects\n" . $herecurr); - } +# if ($^V && $^V ge 5.10.0 && +# $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && +# ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || +# $line =~ /\b__weak\b/)) { +# ERROR("WEAK_DECLARATION", +# "Using weak declarations can have unintended link defects\n" . $herecurr); +# } # check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && 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); diff --git a/zebra/label_manager.c b/zebra/label_manager.c index ad881b819c..38869e80ec 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -363,7 +363,7 @@ int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, * @param instance Instance, to identify the owner * @return Number of chunks released */ -int release_daemon_chunks(uint8_t proto, unsigned short instance) +int release_daemon_label_chunks(uint8_t proto, unsigned short instance) { struct listnode *node; struct label_manager_chunk *lmc; diff --git a/zebra/label_manager.h b/zebra/label_manager.h index a26e195b71..4395e6897e 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -69,7 +69,7 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, uint8_t keep, uint32_t size); int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, uint32_t end); -int release_daemon_chunks(uint8_t proto, unsigned short instance); +int release_daemon_label_chunks(uint8_t proto, unsigned short instance); void label_manager_close(void); #endif /* _LABEL_MANAGER_H */ diff --git a/zebra/subdir.am b/zebra/subdir.am index ef157b7539..9dbff7d40c 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -70,6 +70,7 @@ zebra_zebra_SOURCES = \ zebra/zserv.c \ zebra/zebra_netns_id.c \ zebra/zebra_netns_notify.c \ + zebra/table_manager.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -113,6 +114,7 @@ noinst_HEADERS += \ zebra/zserv.h \ zebra/zebra_netns_id.h \ zebra/zebra_netns_notify.h \ + zebra/table_manager.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/table_manager.c b/zebra/table_manager.c new file mode 100644 index 0000000000..db07f402f3 --- /dev/null +++ b/zebra/table_manager.c @@ -0,0 +1,235 @@ +/* zebra table Manager for routing table identifier management + * Copyright (C) 2018 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include "zebra.h" +#include "zserv.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/table.h" +#include "lib/network.h" +#include "lib/stream.h" +#include "lib/zclient.h" +#include "lib/libfrr.h" +#include "lib/vrf.h" + +#include "zebra_vrf.h" +#include "label_manager.h" /* for NO_PROTO */ +#include "table_manager.h" + +/* routing table identifiers + * + */ +#ifdef SUNOS_5 +/* SunOS + */ +#else +#if !defined(GNU_LINUX) && !defined(SUNOS_5) +/* BSD systems + */ +#else +/* Linux Systems + */ +#define RT_TABLE_ID_LOCAL 255 +#define RT_TABLE_ID_MAIN 254 +#define RT_TABLE_ID_DEFAULT 253 +#define RT_TABLE_ID_COMPAT 252 +#define RT_TABLE_ID_UNSPEC 0 +#endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */ +#endif /* SUNOS_5 */ +#define RT_TABLE_ID_UNRESERVED_MIN 1 +#define RT_TABLE_ID_UNRESERVED_MAX 0xffffffff + +struct table_manager tbl_mgr; + +DEFINE_MGROUP(TABLE_MGR, "Table Manager"); +DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk"); + +static void delete_table_chunk(void *val) +{ + XFREE(MTYPE_TM_CHUNK, val); +} + +/** + * Init table manager + */ +void table_manager_enable(ns_id_t ns_id) +{ + if (ns_id != NS_DEFAULT) + return; + tbl_mgr.lc_list = list_new(); + tbl_mgr.lc_list->del = delete_table_chunk; +} + +/** + * Core function, assigns table chunks + * + * It first searches through the list to check if there's one available + * (previously released). Otherwise it creates and assigns a new one + * + * @param proto Daemon protocol of client, to identify the owner + * @param instance Instance, to identify the owner + * @para size Size of the table chunk + * @return Pointer to the assigned table chunk + */ +struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance, + uint32_t size) +{ + struct table_manager_chunk *tmc; + struct listnode *node; + uint32_t start; + + /* first check if there's one available */ + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->proto == NO_PROTO + && tmc->end - tmc->start + 1 == size) { + tmc->proto = proto; + tmc->instance = instance; + return tmc; + } + } + /* otherwise create a new one */ + tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk)); + if (!tmc) + return NULL; + + /* table RT IDs range are [1;252] and [256;0xffffffff] + * - check if the requested range can be within the first range, + * otherwise elect second one + * - TODO : vrf-lites have their own table identifier. + * In that case, table_id should be removed from the table range. + */ + if (list_isempty(tbl_mgr.lc_list)) + start = RT_TABLE_ID_UNRESERVED_MIN; + else + start = ((struct table_manager_chunk *)listgetdata( + listtail(tbl_mgr.lc_list)))->end + 1; + +#ifdef SUNOS_5 +/* SunOS + */ +#else +#if !defined(GNU_LINUX) && !defined(SUNOS_5) +/* BSD systems + */ +#else +/* Linux Systems + */ + /* if not enough room space between MIN and COMPAT, + * then begin after LOCAL + */ + if (start < RT_TABLE_ID_COMPAT && (size > + RT_TABLE_ID_COMPAT + - RT_TABLE_ID_UNRESERVED_MIN)) + start = RT_TABLE_ID_LOCAL + 1; +#endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */ +#endif /* SUNOS_5 */ + tmc->start = start; + if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) { + zlog_err("Reached max table id. Start/Size %u/%u", + start, size); + XFREE(MTYPE_TM_CHUNK, tmc); + return NULL; + } + tmc->end = tmc->start + size - 1; + tmc->proto = proto; + tmc->instance = instance; + listnode_add(tbl_mgr.lc_list, tmc); + + return tmc; +} + +/** + * Core function, release no longer used table chunks + * + * @param proto Daemon protocol of client, to identify the owner + * @param instance Instance, to identify the owner + * @param start First table RT ID of the chunk + * @param end Last table RT ID of the chunk + * @return 0 on success, -1 otherwise + */ +int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end) +{ + struct listnode *node; + struct table_manager_chunk *tmc; + int ret = -1; + + /* check that size matches */ + zlog_debug("Releasing table chunk: %u - %u", start, end); + /* find chunk and disown */ + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->start != start) + continue; + if (tmc->end != end) + continue; + if (tmc->proto != proto || tmc->instance != instance) { + zlog_err("%s: Daemon mismatch!!", __func__); + continue; + } + tmc->proto = NO_PROTO; + tmc->instance = 0; + ret = 0; + break; + } + if (ret != 0) + zlog_err("%s: Table chunk not released!!", __func__); + + return ret; +} + +/** + * Release table chunks from a client. + * + * Called on client disconnection or reconnection. It only releases chunks + * with empty keep value. + * + * @param proto Daemon protocol of client, to identify the owner + * @param instance Instance, to identify the owner + * @return Number of chunks released + */ +int release_daemon_table_chunks(uint8_t proto, uint16_t instance) +{ + struct listnode *node; + struct table_manager_chunk *tmc; + int count = 0; + int ret; + + for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) { + if (tmc->proto == proto && tmc->instance == instance) { + ret = release_table_chunk(tmc->proto, tmc->instance, + tmc->start, tmc->end); + if (ret == 0) + count++; + } + } + + zlog_debug("%s: Released %d table chunks", __func__, count); + + return count; +} + +void table_manager_disable(ns_id_t ns_id) +{ + if (ns_id != NS_DEFAULT) + return; + list_delete_and_null(&tbl_mgr.lc_list); +} diff --git a/zebra/table_manager.h b/zebra/table_manager.h new file mode 100644 index 0000000000..527d5c29e8 --- /dev/null +++ b/zebra/table_manager.h @@ -0,0 +1,63 @@ +/* zebra table Manager for routing table identifier management + * Copyright (C) 2018 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 _TABLE_MANAGER_H +#define _TABLE_MANAGER_H + +#include <stdint.h> + +#include "lib/linklist.h" +#include "lib/thread.h" + +/* + * Table chunk struct + * Client daemon which the chunk belongs to can be identified by either + * proto (daemon protocol) + instance + VRF. + * If the client then passes a non-empty value to keep field when it requests + * for chunks, the chunks won't be garbage collected and the client will be + * responsible of its release. + * Otherwise, if the keep field is not set (value 0) for the chunk, it will be + * automatically released when the client disconnects or when it reconnects + * (in case it died unexpectedly, we can know it's the same because it will have + * the same proto and instance values) + */ +struct table_manager_chunk { + vrf_id_t vrf_id; + uint8_t proto; + uint16_t instance; + uint32_t start; /* First table RT ID of the chunk */ + uint32_t end; /* Last table RT ID of the chunk */ +}; + +/* + * Main table manager struct + * Holds a linked list of table chunks. + */ +struct table_manager { + struct list *lc_list; +}; + +void table_manager_enable(ns_id_t ns_id); +struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance, + uint32_t size); +int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start, + uint32_t end); +int release_daemon_table_chunks(uint8_t proto, uint16_t instance); +void table_manager_disable(ns_id_t ns_id); + +#endif /* _TABLE_MANAGER_H */ diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index e661b6efc7..ebd632270c 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -163,6 +163,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; msg->key = fpm_route_key_create(allocator, rib_dest_prefix(dest)); qpb_protocol_set(&msg->protocol, re->type); + msg->has_route_type = 1; msg->route_type = FPM__ROUTE_TYPE__NORMAL; msg->metric = re->metric; @@ -245,6 +246,7 @@ static Fpm__Message *create_route_message(qpb_allocator_t *allocator, fpm__message__init(msg); if (!re) { + msg->has_type = 1; msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE; msg->delete_route = create_delete_route_message(allocator, dest, re); @@ -255,6 +257,7 @@ static Fpm__Message *create_route_message(qpb_allocator_t *allocator, return msg; } + msg->has_type = 1; msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE; msg->add_route = create_add_route_message(allocator, dest, re); if (!msg->add_route) { diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 66b1131e39..7393f767af 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -38,6 +38,7 @@ #include "zebra_netns_id.h" #include "zebra_pbr.h" #include "rib.h" +#include "table_manager.h" extern struct zebra_privs_t zserv_privs; @@ -147,6 +148,9 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) interface_list(zns); route_read(zns); + /* Initiate Table Manager per ZNS */ + table_manager_enable(ns_id); + return 0; } @@ -259,6 +263,8 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) kernel_terminate(zns); + table_manager_disable(zns->ns_id); + zns->ns_id = NS_DEFAULT; return 0; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index dfb02f15a9..fe1b100575 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -122,8 +122,8 @@ static int zebra_vrf_enable(struct vrf *vrf) /* Inform clients that the VRF is now active. This is an * add for the clients. */ - zebra_vrf_add_update(zvrf); + zebra_vrf_add_update(zvrf); /* Allocate tables */ for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) diff --git a/zebra/zserv.c b/zebra/zserv.c index 538487a9cd..645deac277 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -61,6 +61,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" +#include "zebra/table_manager.h" /* Event list of zebra. */ enum event { ZEBRA_READ, ZEBRA_WRITE }; @@ -2186,6 +2187,60 @@ stream_failure: return; } +static int zsend_table_manager_connect_response(struct zserv *client, + vrf_id_t vrf_id, uint16_t result) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_TABLE_MANAGER_CONNECT, vrf_id); + + /* result */ + stream_putc(s, result); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zebra_server_send_message(client, s); +} + +/* Send response to a table manager connect request to client */ +static void zread_table_manager_connect(struct zserv *client, + struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s; + uint8_t proto; + uint16_t instance; + + s = msg; + + /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); + + /* accept only dynamic routing protocols */ + if ((proto >= ZEBRA_ROUTE_MAX) || (proto <= ZEBRA_ROUTE_STATIC)) { + zlog_err("client %d has wrong protocol %s", client->sock, + zebra_route_string(proto)); + zsend_table_manager_connect_response(client, vrf_id, 1); + return; + } + zlog_notice("client %d with vrf %u instance %u connected as %s", + client->sock, vrf_id, instance, zebra_route_string(proto)); + client->proto = proto; + client->instance = instance; + + /* + * Release previous labels of same protocol and instance. + * This is done in case it restarted from an unexpected shutdown. + */ + release_daemon_table_chunks(proto, instance); + + zsend_table_manager_connect_response(client, vrf_id, 0); + + stream_failure: + return; +} + static void zread_label_manager_connect(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) { @@ -2217,7 +2272,7 @@ static void zread_label_manager_connect(struct zserv *client, Release previous labels of same protocol and instance. This is done in case it restarted from an unexpected shutdown. */ - release_daemon_chunks(proto, instance); + release_daemon_label_chunks(proto, instance); zlog_debug( " Label Manager client connected: sock %d, proto %s, vrf %u instance %u", @@ -2225,7 +2280,7 @@ static void zread_label_manager_connect(struct zserv *client, /* send response back */ zsend_label_manager_connect_response(client, vrf_id, 0); -stream_failure: + stream_failure: return; } @@ -2305,6 +2360,92 @@ static void zread_label_manager_request(ZAPI_HANDLER_ARGS) } } +/* Send response to a get table chunk request to client */ +static int zsend_assign_table_chunk_response(struct zserv *client, + vrf_id_t vrf_id, + struct table_manager_chunk *tmc) +{ + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_GET_TABLE_CHUNK, vrf_id); + + if (tmc) { + /* start and end labels */ + stream_putl(s, tmc->start); + stream_putl(s, tmc->end); + } + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zebra_server_send_message(client, s); +} + +static void zread_get_table_chunk(struct zserv *client, struct stream *msg, + vrf_id_t vrf_id) +{ + struct stream *s; + uint32_t size; + struct table_manager_chunk *tmc; + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, size); + + tmc = assign_table_chunk(client->proto, client->instance, size); + if (!tmc) + zlog_err("%s: Unable to assign Table Chunk of size %u", + __func__, size); + else + zlog_debug("Assigned Table Chunk %u - %u", tmc->start, + tmc->end); + /* send response back */ + zsend_assign_table_chunk_response(client, vrf_id, tmc); + +stream_failure: + return; +} + +static void zread_release_table_chunk(struct zserv *client, struct stream *msg) +{ + struct stream *s; + uint32_t start, end; + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, start); + STREAM_GETL(s, end); + + release_table_chunk(client->proto, client->instance, start, end); + +stream_failure: + return; +} + +static void zread_table_manager_request(ZAPI_HANDLER_ARGS) +{ + /* to avoid sending other messages like ZERBA_INTERFACE_UP */ + if (hdr->command == ZEBRA_TABLE_MANAGER_CONNECT) + zread_table_manager_connect(client, msg, zvrf_id(zvrf)); + else { + /* Sanity: don't allow 'unidentified' requests */ + if (!client->proto) { + zlog_err( + "Got table request from an unidentified client"); + return; + } + if (hdr->command == ZEBRA_GET_TABLE_CHUNK) + zread_get_table_chunk(client, msg, + zvrf_id(zvrf)); + else if (hdr->command == ZEBRA_RELEASE_TABLE_CHUNK) + zread_release_table_chunk(client, msg); + } +} + static void zread_pseudowire(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -2627,6 +2768,9 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_PW_UNSET] = zread_pseudowire, [ZEBRA_RULE_ADD] = zread_rule, [ZEBRA_RULE_DELETE] = zread_rule, + [ZEBRA_TABLE_MANAGER_CONNECT] = zread_table_manager_request, + [ZEBRA_GET_TABLE_CHUNK] = zread_table_manager_request, + [ZEBRA_RELEASE_TABLE_CHUNK] = zread_table_manager_request, }; static inline void zserv_handle_commands(struct zserv *client, @@ -2658,7 +2802,10 @@ static void zebra_client_free(struct zserv *client) zebra_client_close_cleanup_rnh(client); /* Release Label Manager chunks */ - release_daemon_chunks(client->proto, client->instance); + release_daemon_label_chunks(client->proto, client->instance); + + /* Release Table Manager chunks */ + release_daemon_table_chunks(client->proto, client->instance); /* Cleanup any FECs registered by this client. */ zebra_mpls_cleanup_fecs_for_client(vrf_info_lookup(VRF_DEFAULT), |
