summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/Makefile.am5
-rw-r--r--bgpd/bgp_attr.c27
-rw-r--r--bgpd/bgp_debug.c24
-rw-r--r--bgpd/bgp_debug.h3
-rw-r--r--bgpd/bgp_ecommunity.c66
-rw-r--r--bgpd/bgp_ecommunity.h11
-rw-r--r--bgpd/bgp_flowspec.c195
-rw-r--r--bgpd/bgp_flowspec.h50
-rw-r--r--bgpd/bgp_flowspec_private.h44
-rw-r--r--bgpd/bgp_flowspec_util.c458
-rw-r--r--bgpd/bgp_flowspec_util.h58
-rw-r--r--bgpd/bgp_flowspec_vty.c414
-rw-r--r--bgpd/bgp_memory.c7
-rw-r--r--bgpd/bgp_memory.h8
-rw-r--r--bgpd/bgp_open.c11
-rw-r--r--bgpd/bgp_packet.c7
-rw-r--r--bgpd/bgp_route.c72
-rw-r--r--bgpd/bgp_route.h3
-rw-r--r--bgpd/bgp_vty.c105
-rw-r--r--bgpd/bgp_vty.h3
-rw-r--r--bgpd/bgp_zebra.c58
-rw-r--r--bgpd/bgp_zebra.h3
-rw-r--r--bgpd/bgpd.c25
-rw-r--r--bgpd/bgpd.h9
-rw-r--r--doc/developer/_static/overrides.css4
-rw-r--r--doc/developer/building-frr-on-alpine.rst2
-rw-r--r--doc/developer/conf.py4
-rw-r--r--doc/user/_static/overrides.css4
-rw-r--r--doc/user/bgp.rst28
-rw-r--r--doc/user/conf.py4
-rw-r--r--doc/user/routemap.rst37
-rw-r--r--lib/command.c10
-rw-r--r--lib/command.h2
-rw-r--r--lib/log.c3
-rw-r--r--lib/memory.c1
-rw-r--r--lib/memory.h1
-rw-r--r--lib/prefix.c88
-rw-r--r--lib/prefix.h19
-rw-r--r--lib/table.c12
-rw-r--r--lib/zclient.c162
-rw-r--r--lib/zclient.h9
-rw-r--r--lib/zebra.h10
-rw-r--r--ospf6d/ospf6_abr.c4
-rw-r--r--ospf6d/ospf6_intra.c11
-rw-r--r--ospf6d/ospf6_route.c6
-rw-r--r--tests/bgpd/test_mp_attr.c41
-rwxr-xr-xtools/checkpatch.pl69
-rw-r--r--vtysh/vtysh.c48
-rw-r--r--zebra/label_manager.c2
-rw-r--r--zebra/label_manager.h2
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/table_manager.c235
-rw-r--r--zebra/table_manager.h63
-rw-r--r--zebra/zebra_fpm_protobuf.c3
-rw-r--r--zebra/zebra_ns.c6
-rw-r--r--zebra/zebra_vrf.c2
-rw-r--r--zebra/zserv.c153
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 */
};
diff --git a/lib/log.c b/lib/log.c
index 35298f1fa3..f5aff756dd 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -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(&copy, 0, sizeof(copy));
+ prefix_copy(&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),