summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_flowspec_vty.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /bgpd/bgp_flowspec_vty.c
parentInitial commit. (diff)
downloadfrr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz
frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/bgp_flowspec_vty.c')
-rw-r--r--bgpd/bgp_flowspec_vty.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
new file mode 100644
index 0000000..02dcdfc
--- /dev/null
+++ b/bgpd/bgp_flowspec_vty.c
@@ -0,0 +1,633 @@
+/* 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"
+#include "bgpd/bgp_pbr.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"},
+ {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"},
+ {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, "tcp"},
+ {FLOWSPEC_PKT_LEN, "pktlen"},
+ {FLOWSPEC_DSCP, "dscp"},
+ {FLOWSPEC_FRAGMENT, "pktfrag"},
+ {FLOWSPEC_FLOW_LABEL, "flwlbl"},
+ {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,
+ afi_t afi)
+{
+ 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,
+ afi, NULL);
+ 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_FLOW_LABEL:
+ 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_bitmask_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_bitmask_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, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ json_object *json_paths)
+{
+ struct attr *attr;
+ char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
+ char *s1 = NULL, *s2 = NULL;
+ json_object *json_nlri_path = NULL;
+ json_object *json_ecom_path = NULL;
+ json_object *json_time_path = NULL;
+ char timebuf[BGP_UPTIME_LEN];
+ struct ecommunity *ipv6_ecomm = NULL;
+
+ if (p == NULL || 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 && path)
+ vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n",
+ path->flags);
+ bgp_fs_nlri_get_string((unsigned char *)
+ p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen,
+ return_string,
+ display,
+ json_nlri_path,
+ family2afi(p->u.prefix_flowspec
+ .family));
+ 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 (!path)
+ return;
+
+ if (path->attr)
+ ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(path->attr);
+
+ if (path->attr && (bgp_attr_get_ecommunity(path->attr) || ipv6_ecomm)) {
+ /* Print attribute */
+ attr = path->attr;
+ if (bgp_attr_get_ecommunity(attr))
+ s1 = ecommunity_ecom2str(bgp_attr_get_ecommunity(attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ 0);
+ if (ipv6_ecomm)
+ s2 = ecommunity_ecom2str(
+ ipv6_ecomm, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ if (!s1 && !s2)
+ return;
+ if (display == NLRI_STRING_FORMAT_LARGE)
+ vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "",
+ s2 && s1 ? " " : "", s2 ? s2 : "");
+ else if (display == NLRI_STRING_FORMAT_MIN)
+ vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : "");
+ else if (json_paths) {
+ json_ecom_path = json_object_new_object();
+ if (s1)
+ json_object_string_add(json_ecom_path,
+ "ecomlist", s1);
+ if (s2)
+ json_object_string_add(json_ecom_path,
+ "ecom6list", s2);
+ if (display == NLRI_STRING_FORMAT_JSON)
+ json_object_array_add(json_paths,
+ json_ecom_path);
+ }
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ char local_buff[INET6_ADDRSTRLEN];
+
+ local_buff[0] = '\0';
+ if (p->u.prefix_flowspec.family == AF_INET
+ && attr->nexthop.s_addr != INADDR_ANY)
+ inet_ntop(AF_INET,
+ &attr->nexthop.s_addr,
+ local_buff,
+ INET6_ADDRSTRLEN);
+ else if (p->u.prefix_flowspec.family == AF_INET6 &&
+ attr->mp_nexthop_len != 0 &&
+ attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 &&
+ attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4)
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global,
+ local_buff,
+ INET6_ADDRSTRLEN);
+ if (local_buff[0] != '\0')
+ vty_out(vty, "\tNLRI NH %s\n",
+ local_buff);
+ }
+ XFREE(MTYPE_ECOMMUNITY_STR, s1);
+ XFREE(MTYPE_ECOMMUNITY_STR, s2);
+ }
+ peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ vty_out(vty, "\treceived 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);
+ }
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ struct bgp_path_info_extra *extra =
+ bgp_path_info_extra_get(path);
+ bool list_began = false;
+
+ if (extra->bgp_fs_pbr && listcount(extra->bgp_fs_pbr)) {
+ struct listnode *node;
+ struct bgp_pbr_match_entry *bpme;
+ struct bgp_pbr_match *bpm;
+ struct list *list_bpm;
+
+ list_bpm = list_new();
+ vty_out(vty, "\tinstalled in PBR");
+ for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_pbr,
+ node, bpme)) {
+ bpm = bpme->backpointer;
+ if (listnode_lookup(list_bpm, bpm))
+ continue;
+ listnode_add(list_bpm, bpm);
+ if (!list_began) {
+ vty_out(vty, " (");
+ list_began = true;
+ } else
+ vty_out(vty, ", ");
+ vty_out(vty, "%s", bpm->ipset_name);
+ }
+ list_delete(&list_bpm);
+ }
+ if (extra->bgp_fs_iprule && listcount(extra->bgp_fs_iprule)) {
+ struct listnode *node;
+ struct bgp_pbr_rule *bpr;
+
+ if (!list_began)
+ vty_out(vty, "\tinstalled in PBR");
+ for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_iprule,
+ node, bpr)) {
+ if (!bpr->action)
+ continue;
+ if (!list_began) {
+ vty_out(vty, " (");
+ list_began = true;
+ } else
+ vty_out(vty, ", ");
+ vty_out(vty, "-ipv4-rule %d action lookup %u-",
+ bpr->priority,
+ bpr->action->table_id);
+ }
+ }
+ if (list_began)
+ vty_out(vty, ")\n");
+ else
+ vty_out(vty, "\tnot installed in PBR\n");
+ }
+}
+
+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, bool use_json, int is_last,
+ unsigned long *output_cum, unsigned long *total_cum)
+{
+ struct bgp_path_info *pi;
+ struct bgp_dest *dest;
+ 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 (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi == NULL)
+ continue;
+ if (use_json) {
+ json_paths = json_object_new_array();
+ display = NLRI_STRING_FORMAT_JSON;
+ }
+ for (; pi; pi = pi->next) {
+ total_count++;
+ route_vty_out_flowspec(vty, bgp_dest_get_prefix(dest),
+ pi, display, json_paths);
+ }
+ if (use_json) {
+ vty_json(vty, 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;
+}
+
+int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_pbr_interface *pbr_if;
+ bool declare_node = false;
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ bool bgp_pbr_interface_any;
+
+ if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC)
+ return 0;
+ if (afi == AFI_IP) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4;
+ } else if (afi == AFI_IP6) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6;
+ } else {
+ return 0;
+ }
+ if (!RB_EMPTY(bgp_pbr_interface_head, head) ||
+ !bgp_pbr_interface_any)
+ declare_node = true;
+ RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
+ vty_out(vty, " local-install %s\n", pbr_if->name);
+ }
+ return declare_node ? 1 : 0;
+}
+
+static int bgp_fs_local_install_interface(struct bgp *bgp,
+ const char *no, const char *ifname,
+ afi_t afi)
+{
+ struct bgp_pbr_interface *pbr_if;
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ bool *bgp_pbr_interface_any;
+
+ if (!bgp_pbr_cfg)
+ return CMD_SUCCESS;
+ if (afi == AFI_IP) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4);
+ } else {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6);
+ }
+ if (no) {
+ if (!ifname) {
+ if (*bgp_pbr_interface_any) {
+ *bgp_pbr_interface_any = false;
+ /* remove all other interface list */
+ bgp_pbr_reset(bgp, afi);
+ }
+ return CMD_SUCCESS;
+ }
+ pbr_if = bgp_pbr_interface_lookup(ifname, head);
+ if (!pbr_if)
+ return CMD_SUCCESS;
+ RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
+ return CMD_SUCCESS;
+ }
+ if (ifname) {
+ pbr_if = bgp_pbr_interface_lookup(ifname, head);
+ if (pbr_if)
+ return CMD_SUCCESS;
+ pbr_if = XCALLOC(MTYPE_TMP,
+ sizeof(struct bgp_pbr_interface));
+ strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ);
+ RB_INSERT(bgp_pbr_interface_head, head, pbr_if);
+ *bgp_pbr_interface_any = false;
+ } else {
+ /* set to default */
+ if (!*bgp_pbr_interface_any) {
+ /* remove all other interface list
+ */
+ bgp_pbr_reset(bgp, afi);
+ *bgp_pbr_interface_any = true;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_fs_local_install_ifname,
+ bgp_fs_local_install_ifname_cmd,
+ "[no] local-install INTERFACE",
+ NO_STR
+ "Apply local policy routing\n"
+ "Interface name\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int idx = 0;
+ const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
+ char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ?
+ argv[idx]->arg : NULL;
+
+ return bgp_fs_local_install_interface(bgp, no, ifname,
+ bgp_node_afi(vty));
+}
+
+extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib,
+ struct prefix *match,
+ int prefix_check, struct vty *vty,
+ bool use_json,
+ json_object *json_paths)
+{
+ struct bgp_dest *dest;
+ const struct prefix *prefix;
+ int display = 0;
+
+ for (dest = bgp_table_top(rib); dest; dest = bgp_route_next(dest)) {
+ prefix = bgp_dest_get_prefix(dest);
+
+ if (prefix->family != AF_FLOWSPEC)
+ continue;
+
+ if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) {
+ route_vty_out_flowspec(
+ vty, prefix, bgp_dest_get_bgp_path_info(dest),
+ use_json ? NLRI_STRING_FORMAT_JSON
+ : NLRI_STRING_FORMAT_LARGE,
+ json_paths);
+ display++;
+ }
+ }
+ return display;
+}
+
+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);
+ install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd);
+}