diff options
author | Philippe Guibert <philippe.guibert@6wind.com> | 2018-04-25 18:29:35 +0200 |
---|---|---|
committer | Philippe Guibert <philippe.guibert@6wind.com> | 2018-05-25 15:49:38 +0200 |
commit | 4762c2137de994b5b1054cc43c26efef04b27bbf (patch) | |
tree | e645d9a0e0938327506bf7b7bfd507a28329de57 /bgpd | |
parent | bgpd: display if FS entry is installed in PBR or not (diff) | |
download | frr-4762c2137de994b5b1054cc43c26efef04b27bbf.tar.xz frr-4762c2137de994b5b1054cc43c26efef04b27bbf.zip |
bgpd: add vty command to restrict FS policy routing to a defined interface
policy routing is configurable via address-family ipv4 flowspec
subfamily node. This is then possible to restrict flowspec operation
through the BGP instance, to a single or some interfaces, but not all.
Two commands available:
[no] local-install [IFNAME]
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/bgp_flowspec.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_flowspec_vty.c | 103 | ||||
-rw-r--r-- | bgpd/bgp_pbr.c | 50 | ||||
-rw-r--r-- | bgpd/bgp_pbr.h | 25 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 42 | ||||
-rw-r--r-- | bgpd/bgpd.c | 3 | ||||
-rw-r--r-- | bgpd/bgpd.h | 3 |
7 files changed, 228 insertions, 1 deletions
diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 392b32153..5dd2c3931 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -47,4 +47,7 @@ extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, extern void route_vty_out_flowspec(struct vty *vty, struct prefix *p, struct bgp_info *binfo, int display, json_object *json_paths); +extern int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi); + #endif /* _FRR_BGP_FLOWSPEC_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 3ff452889..b21e5ae0d 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -425,10 +425,113 @@ DEFUN (no_debug_bgp_flowspec, 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 || afi != AFI_IP) + return 0; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + 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); + } + if (!bgp_pbr_interface_any) + vty_out(vty, " no local-install any\n"); + return declare_node ? 1 : 0; +} + +static int bgp_fs_local_install_interface(struct bgp *bgp, + const char *no, const char *ifname) +{ + 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; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + if (no) { + if (!ifname) { + if (*bgp_pbr_interface_any) { + *bgp_pbr_interface_any = false; + /* remove all other interface list */ + bgp_pbr_reset(bgp, AFI_IP); + } + 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_IP); + *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, (char *)"no") ? "no" : NULL; + char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? + argv[idx]->arg : NULL; + + return bgp_fs_local_install_interface(bgp, no, ifname); +} + +DEFUN (bgp_fs_local_install_any, + bgp_fs_local_install_any_cmd, + "[no] local-install any", + NO_STR + "Apply local policy routing\n" + "Any Interface\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + const char *no = strmatch(argv[0]->text, (char *)"no") ? "no" : NULL; + + return bgp_fs_local_install_interface(bgp, no, NULL); +} + 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_any_cmd); + install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); } diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 646d58352..343a928ad 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -35,6 +35,12 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry") DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match") DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action") +DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context") + +RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, + id_entry, bgp_pbr_interface_compare); +struct bgp_pbr_interface_head ifaces_by_name_ipv4 = + RB_INITIALIZER(&ifaces_by_name_ipv4); static int bgp_pbr_match_counter_unique; static int bgp_pbr_match_entry_counter_unique; @@ -705,6 +711,11 @@ void bgp_pbr_cleanup(struct bgp *bgp) hash_free(bgp->pbr_action_hash); bgp->pbr_action_hash = NULL; } + if (bgp->bgp_pbr_cfg == NULL) + return; + bgp_pbr_reset(bgp, AFI_IP); + XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); + bgp->bgp_pbr_cfg = NULL; } void bgp_pbr_init(struct bgp *bgp) @@ -717,6 +728,9 @@ void bgp_pbr_init(struct bgp *bgp) hash_create_size(8, bgp_pbr_action_hash_key, bgp_pbr_action_hash_equal, "Match Hash Entry"); + + bgp->bgp_pbr_cfg = XCALLOC(MTYPE_PBR, sizeof(struct bgp_pbr_config)); + bgp->bgp_pbr_cfg->pbr_interface_any_ipv4 = true; } void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) @@ -1350,3 +1364,39 @@ void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, } bgp_pbr_handle_entry(bgp, info, &api, nlri_update); } + +int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, + const struct bgp_pbr_interface *b) +{ + return strcmp(a->name, b->name); +} + +struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name, + struct bgp_pbr_interface_head *head) +{ + struct bgp_pbr_interface pbr_if; + + strlcpy(pbr_if.name, name, sizeof(pbr_if.name)); + return (RB_FIND(bgp_pbr_interface_head, + head, &pbr_if)); +} + +/* this function resets to the default policy routing + * go back to default status + */ +void bgp_pbr_reset(struct bgp *bgp, afi_t afi) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + + if (!bgp_pbr_cfg || afi != AFI_IP) + return; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + while (!RB_EMPTY(bgp_pbr_interface_head, head)) { + pbr_if = RB_ROOT(bgp_pbr_interface_head, head); + RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); + XFREE(MTYPE_TMP, pbr_if); + } +} diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index e4117ffd0..20edaf30b 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -150,6 +150,25 @@ struct bgp_pbr_entry_main { vrf_id_t vrf_id; }; +struct bgp_pbr_interface { + RB_ENTRY(bgp_pbr_interface) id_entry; + char name[INTERFACE_NAMSIZ]; +}; + +RB_HEAD(bgp_pbr_interface_head, bgp_pbr_interface); +RB_PROTOTYPE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, + bgp_pbr_interface_compare); + +extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, + const struct bgp_pbr_interface *b); + +struct bgp_pbr_config { + struct bgp_pbr_interface_head ifaces_by_name_ipv4; + bool pbr_interface_any_ipv4; +}; + +extern struct bgp_pbr_config *bgp_pbr_cfg; + struct bgp_pbr_match { char ipset_name[ZEBRA_IPSET_NAME_SIZE]; @@ -267,4 +286,10 @@ extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, bool nlri_update); +/* bgp pbr utilities */ +extern struct bgp_pbr_interface *pbr_interface_lookup(const char *name); +extern void bgp_pbr_reset(struct bgp *bgp, afi_t afi); +extern struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name, + struct bgp_pbr_interface_head *head); + #endif /* __BGP_PBR_H__ */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 84a815580..641861a28 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2574,12 +2574,49 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, pbrime->install_in_progress = true; } +static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + struct interface *ifp; + + if (!bgp_pbr_cfg) + return; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { + ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); + if (ifp) + stream_putl(s, ifp->ifindex); + } +} + +static int bgp_pbr_get_ifnumber(struct bgp *bgp) +{ + struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; + struct bgp_pbr_interface_head *head; + struct bgp_pbr_interface *pbr_if; + int cnt = 0; + + if (!bgp_pbr_cfg) + return 0; + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + + RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { + if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) + cnt++; + } + return cnt; +} + void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, struct bgp_pbr_match *pbm, bool install) { struct stream *s; int ret = 0; + int nb_interface; if (pbm->install_iptable_in_progress) return; @@ -2594,7 +2631,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, VRF_DEFAULT); bgp_encode_pbr_iptable_match(s, pba, pbm); - + nb_interface = bgp_pbr_get_ifnumber(pba->bgp); + stream_putl(s, nb_interface); + if (nb_interface) + bgp_encode_pbr_interface_list(pba->bgp, s); stream_putw_at(s, 0, stream_get_endp(s)); ret = zclient_send_message(zclient); if (install) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 71707b6af..ab13a37d9 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -7266,6 +7266,9 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, if (safi == SAFI_EVPN) bgp_config_write_evpn_info(vty, bgp, afi, safi); + if (safi == SAFI_FLOWSPEC) + bgp_fs_config_write_pbr(vty, bgp, afi, safi); + if (safi == SAFI_UNICAST) { bgp_vpn_policy_config_write_afi(vty, bgp, afi); if (CHECK_FLAG(bgp->af_flags[afi][safi], diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 340851e8d..f663df162 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -47,6 +47,7 @@ struct update_subgroup; struct bpacket; +struct bgp_pbr_config; /* * Allow the neighbor XXXX remote-as to take internal or external @@ -531,6 +532,8 @@ struct bgp { struct vpn_policy vpn_policy[AFI_MAX]; + struct bgp_pbr_config *bgp_pbr_cfg; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) |