mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 13:41:46 +00:00
bgpd: Add neighbor soo
command
BGP SoO is a tag that is appended on BGP updates to allow a peer to mark a particular peer as belonging to a particular site. In certain MPLS L3 VPN configurations, the BGP AS-Path may not provide the granularity needed prevent a loop in the control-plane. With this in mind, BGP SoO is designed to fill this gap and prevent a routing loop that may occur. If we configure for example, `neighbor soo 65000:1` at PEs, routes won't be announced between CPEs if soo matches. This is especially needed when using as-override or allowas-in. Also, this is the automated way of the same behavior as configuring route-maps for each peer like: ``` bgp extcommunity-list cpe permit soo 65000:1 ! route-map cpe permit 10 set extcommunity soo 65000:1 ... route-map cpe deny 10 match extcommunity cpe route-map cpe permit 20 ... ``` Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
parent
a9f3f4f526
commit
01da2d2691
@ -300,6 +300,11 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
|
|||||||
peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
|
peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
|
||||||
peer->orf_plist[afi][safi] = from_peer->orf_plist[afi][safi];
|
peer->orf_plist[afi][safi] = from_peer->orf_plist[afi][safi];
|
||||||
peer->llgr[afi][safi] = from_peer->llgr[afi][safi];
|
peer->llgr[afi][safi] = from_peer->llgr[afi][safi];
|
||||||
|
if (from_peer->soo[afi][safi]) {
|
||||||
|
ecommunity_free(&peer->soo[afi][safi]);
|
||||||
|
peer->soo[afi][safi] =
|
||||||
|
ecommunity_dup(from_peer->soo[afi][safi]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bgp_getsockname(peer) < 0) {
|
if (bgp_getsockname(peer) < 0) {
|
||||||
|
@ -2316,6 +2316,29 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
|
|||||||
if (aspath_check_as_sets(attr->aspath))
|
if (aspath_check_as_sets(attr->aspath))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* If neighbor sso is configured, then check if the route has
|
||||||
|
* SoO extended community and validate against the configured
|
||||||
|
* one. If they match, do not announce, to prevent routing
|
||||||
|
* loops.
|
||||||
|
*/
|
||||||
|
if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) &&
|
||||||
|
peer->soo[afi][safi]) {
|
||||||
|
struct ecommunity *ecomm_soo = peer->soo[afi][safi];
|
||||||
|
struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
|
||||||
|
|
||||||
|
if ((ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_AS,
|
||||||
|
ECOMMUNITY_SITE_ORIGIN) ||
|
||||||
|
ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_AS4,
|
||||||
|
ECOMMUNITY_SITE_ORIGIN)) &&
|
||||||
|
ecommunity_include(ecomm, ecomm_soo)) {
|
||||||
|
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
|
||||||
|
zlog_debug(
|
||||||
|
"%pBP [Update:SEND] %pFX is filtered by SoO extcommunity '%s'",
|
||||||
|
peer, p, ecommunity_str(ecomm_soo));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Codification of AS 0 Processing */
|
/* Codification of AS 0 Processing */
|
||||||
if (aspath_check_as_zero(attr->aspath))
|
if (aspath_check_as_zero(attr->aspath))
|
||||||
return false;
|
return false;
|
||||||
@ -4057,6 +4080,30 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If neighbor soo is configured, tag all incoming routes with
|
||||||
|
* this SoO tag and then filter out advertisements in
|
||||||
|
* subgroup_announce_check() if it matches the configured SoO
|
||||||
|
* on the other peer.
|
||||||
|
*/
|
||||||
|
if (peer->soo[afi][safi]) {
|
||||||
|
struct ecommunity *old_ecomm =
|
||||||
|
bgp_attr_get_ecommunity(&new_attr);
|
||||||
|
struct ecommunity *ecomm_soo = peer->soo[afi][safi];
|
||||||
|
struct ecommunity *new_ecomm;
|
||||||
|
|
||||||
|
if (old_ecomm) {
|
||||||
|
new_ecomm = ecommunity_merge(ecommunity_dup(old_ecomm),
|
||||||
|
ecomm_soo);
|
||||||
|
|
||||||
|
if (!old_ecomm->refcnt)
|
||||||
|
ecommunity_free(&old_ecomm);
|
||||||
|
} else {
|
||||||
|
new_ecomm = ecommunity_dup(ecomm_soo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bgp_attr_set_ecommunity(&new_attr, new_ecomm);
|
||||||
|
}
|
||||||
|
|
||||||
attr_new = bgp_attr_intern(&new_attr);
|
attr_new = bgp_attr_intern(&new_attr);
|
||||||
|
|
||||||
/* If the update is implicit withdraw. */
|
/* If the update is implicit withdraw. */
|
||||||
|
@ -164,6 +164,12 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
|
|||||||
dst->change_local_as = src->change_local_as;
|
dst->change_local_as = src->change_local_as;
|
||||||
dst->shared_network = src->shared_network;
|
dst->shared_network = src->shared_network;
|
||||||
dst->local_role = src->local_role;
|
dst->local_role = src->local_role;
|
||||||
|
|
||||||
|
if (src->soo[afi][safi]) {
|
||||||
|
ecommunity_free(&dst->soo[afi][safi]);
|
||||||
|
dst->soo[afi][safi] = ecommunity_dup(src->soo[afi][safi]);
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&(dst->nexthop), &(src->nexthop), sizeof(struct bgp_nexthop));
|
memcpy(&(dst->nexthop), &(src->nexthop), sizeof(struct bgp_nexthop));
|
||||||
|
|
||||||
dst->group = src->group;
|
dst->group = src->group;
|
||||||
@ -428,6 +434,12 @@ static unsigned int updgrp_hash_key_make(const void *p)
|
|||||||
*/
|
*/
|
||||||
key = jhash_1word(peer->local_role, key);
|
key = jhash_1word(peer->local_role, key);
|
||||||
|
|
||||||
|
if (peer->soo[afi][safi]) {
|
||||||
|
char *soo_str = ecommunity_str(peer->soo[afi][safi]);
|
||||||
|
|
||||||
|
key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key);
|
||||||
|
}
|
||||||
|
|
||||||
if (bgp_debug_neighbor_events(peer)) {
|
if (bgp_debug_neighbor_events(peer)) {
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %u",
|
"%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %u",
|
||||||
|
@ -8228,6 +8228,63 @@ ALIAS_HIDDEN(
|
|||||||
"Only give warning message when limit is exceeded\n"
|
"Only give warning message when limit is exceeded\n"
|
||||||
"Force checking all received routes not only accepted\n")
|
"Force checking all received routes not only accepted\n")
|
||||||
|
|
||||||
|
/* "neighbor soo" */
|
||||||
|
DEFPY (neighbor_soo,
|
||||||
|
neighbor_soo_cmd,
|
||||||
|
"neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor soo ASN:NN_OR_IP-ADDRESS:NN$soo",
|
||||||
|
NEIGHBOR_STR
|
||||||
|
NEIGHBOR_ADDR_STR2
|
||||||
|
"Set the Site-of-Origin (SoO) extended community\n"
|
||||||
|
"VPN extended community\n")
|
||||||
|
{
|
||||||
|
struct peer *peer;
|
||||||
|
afi_t afi = bgp_node_afi(vty);
|
||||||
|
safi_t safi = bgp_node_safi(vty);
|
||||||
|
struct ecommunity *ecomm_soo;
|
||||||
|
|
||||||
|
peer = peer_and_group_lookup_vty(vty, neighbor);
|
||||||
|
if (!peer)
|
||||||
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
|
|
||||||
|
ecomm_soo = ecommunity_str2com(soo, ECOMMUNITY_SITE_ORIGIN, 0);
|
||||||
|
if (!ecomm_soo) {
|
||||||
|
vty_out(vty, "%% Malformed SoO extended community\n");
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
ecommunity_str(ecomm_soo);
|
||||||
|
|
||||||
|
if (!ecommunity_match(peer->soo[afi][safi], ecomm_soo)) {
|
||||||
|
ecommunity_free(&peer->soo[afi][safi]);
|
||||||
|
peer->soo[afi][safi] = ecomm_soo;
|
||||||
|
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_SOO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bgp_vty_return(vty,
|
||||||
|
peer_af_flag_set(peer, afi, safi, PEER_FLAG_SOO));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFPY (no_neighbor_soo,
|
||||||
|
no_neighbor_soo_cmd,
|
||||||
|
"no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor soo [ASN:NN_OR_IP-ADDRESS:NN$soo]",
|
||||||
|
NO_STR
|
||||||
|
NEIGHBOR_STR
|
||||||
|
NEIGHBOR_ADDR_STR2
|
||||||
|
"Set the Site-of-Origin (SoO) extended community\n"
|
||||||
|
"VPN extended community\n")
|
||||||
|
{
|
||||||
|
struct peer *peer;
|
||||||
|
afi_t afi = bgp_node_afi(vty);
|
||||||
|
safi_t safi = bgp_node_safi(vty);
|
||||||
|
|
||||||
|
peer = peer_and_group_lookup_vty(vty, neighbor);
|
||||||
|
if (!peer)
|
||||||
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
|
|
||||||
|
ecommunity_free(&peer->soo[afi][safi]);
|
||||||
|
|
||||||
|
return bgp_vty_return(
|
||||||
|
vty, peer_af_flag_unset(peer, afi, safi, PEER_FLAG_SOO));
|
||||||
|
}
|
||||||
|
|
||||||
/* "neighbor allowas-in" */
|
/* "neighbor allowas-in" */
|
||||||
DEFUN (neighbor_allowas_in,
|
DEFUN (neighbor_allowas_in,
|
||||||
@ -17221,6 +17278,15 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* soo */
|
||||||
|
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) {
|
||||||
|
char *soo_str = ecommunity_ecom2str(
|
||||||
|
peer->soo[afi][safi], ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
||||||
|
|
||||||
|
vty_out(vty, " neighbor %s soo %s\n", addr, soo_str);
|
||||||
|
XFREE(MTYPE_ECOMMUNITY_STR, soo_str);
|
||||||
|
}
|
||||||
|
|
||||||
/* weight */
|
/* weight */
|
||||||
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
|
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
|
||||||
vty_out(vty, " neighbor %s weight %lu\n", addr,
|
vty_out(vty, " neighbor %s weight %lu\n", addr,
|
||||||
@ -19305,6 +19371,26 @@ void bgp_vty_init(void)
|
|||||||
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
|
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
|
||||||
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
|
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
|
||||||
|
|
||||||
|
/* "neighbor soo" */
|
||||||
|
install_element(BGP_IPV4_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV4M_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV4M_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV4L_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV4L_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6M_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6M_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6L_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_IPV6L_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_VPNV4_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_VPNV4_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_VPNV6_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_VPNV6_NODE, &no_neighbor_soo_cmd);
|
||||||
|
install_element(BGP_EVPN_NODE, &neighbor_soo_cmd);
|
||||||
|
install_element(BGP_EVPN_NODE, &no_neighbor_soo_cmd);
|
||||||
|
|
||||||
/* address-family commands. */
|
/* address-family commands. */
|
||||||
install_element(BGP_NODE, &address_family_ipv4_safi_cmd);
|
install_element(BGP_NODE, &address_family_ipv4_safi_cmd);
|
||||||
install_element(BGP_NODE, &address_family_ipv6_safi_cmd);
|
install_element(BGP_NODE, &address_family_ipv6_safi_cmd);
|
||||||
|
12
bgpd/bgpd.c
12
bgpd/bgpd.c
@ -1377,6 +1377,7 @@ struct peer *peer_new(struct bgp *bgp)
|
|||||||
SET_FLAG(peer->af_flags_invert[afi][safi],
|
SET_FLAG(peer->af_flags_invert[afi][safi],
|
||||||
PEER_FLAG_SEND_LARGE_COMMUNITY);
|
PEER_FLAG_SEND_LARGE_COMMUNITY);
|
||||||
peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
|
peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
|
||||||
|
peer->soo[afi][safi] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set nexthop-unchanged for l2vpn evpn by default */
|
/* set nexthop-unchanged for l2vpn evpn by default */
|
||||||
@ -1483,6 +1484,11 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
|
|||||||
peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
|
peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
|
||||||
peer_dst->addpath_type[afi][safi] =
|
peer_dst->addpath_type[afi][safi] =
|
||||||
peer_src->addpath_type[afi][safi];
|
peer_src->addpath_type[afi][safi];
|
||||||
|
if (peer_src->soo[afi][safi]) {
|
||||||
|
ecommunity_free(&peer_dst->soo[afi][safi]);
|
||||||
|
peer_dst->soo[afi][safi] =
|
||||||
|
ecommunity_dup(peer_src->soo[afi][safi]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
|
for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
|
||||||
@ -2042,6 +2048,10 @@ static void peer_group2peer_config_copy_af(struct peer_group *group,
|
|||||||
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
|
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
|
||||||
PEER_ATTR_INHERIT(peer, group, allowas_in[afi][safi]);
|
PEER_ATTR_INHERIT(peer, group, allowas_in[afi][safi]);
|
||||||
|
|
||||||
|
/* soo */
|
||||||
|
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_SOO))
|
||||||
|
PEER_ATTR_INHERIT(peer, group, soo[afi][safi]);
|
||||||
|
|
||||||
/* weight */
|
/* weight */
|
||||||
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_WEIGHT))
|
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_WEIGHT))
|
||||||
PEER_ATTR_INHERIT(peer, group, weight[afi][safi]);
|
PEER_ATTR_INHERIT(peer, group, weight[afi][safi]);
|
||||||
@ -2548,6 +2558,7 @@ int peer_delete(struct peer *peer)
|
|||||||
|
|
||||||
XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
|
XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
|
||||||
XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name);
|
XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name);
|
||||||
|
ecommunity_free(&peer->soo[afi][safi]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FOREACH_AFI_SAFI (afi, safi)
|
FOREACH_AFI_SAFI (afi, safi)
|
||||||
@ -4278,6 +4289,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
|
|||||||
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out},
|
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out},
|
||||||
{PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
|
{PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
|
||||||
{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
|
{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
|
||||||
|
{PEER_FLAG_SOO, 0, peer_change_reset},
|
||||||
{0, 0, 0}};
|
{0, 0, 0}};
|
||||||
|
|
||||||
/* Proper action set. */
|
/* Proper action set. */
|
||||||
|
@ -1411,6 +1411,7 @@ struct peer {
|
|||||||
#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */
|
#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */
|
||||||
#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */
|
#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */
|
||||||
#define PEER_FLAG_DISABLE_ADDPATH_RX (1U << 29) /* disable-addpath-rx */
|
#define PEER_FLAG_DISABLE_ADDPATH_RX (1U << 29) /* disable-addpath-rx */
|
||||||
|
#define PEER_FLAG_SOO (1U << 30) /* soo */
|
||||||
|
|
||||||
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
|
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
|
||||||
|
|
||||||
@ -1620,6 +1621,9 @@ struct peer {
|
|||||||
/* allowas-in. */
|
/* allowas-in. */
|
||||||
char allowas_in[AFI_MAX][SAFI_MAX];
|
char allowas_in[AFI_MAX][SAFI_MAX];
|
||||||
|
|
||||||
|
/* soo */
|
||||||
|
struct ecommunity *soo[AFI_MAX][SAFI_MAX];
|
||||||
|
|
||||||
/* weight */
|
/* weight */
|
||||||
unsigned long weight[AFI_MAX][SAFI_MAX];
|
unsigned long weight[AFI_MAX][SAFI_MAX];
|
||||||
|
|
||||||
|
@ -2832,6 +2832,19 @@ of the global VPNv4/VPNv6 family. This command defaults to on and is not
|
|||||||
displayed.
|
displayed.
|
||||||
The `no bgp retain route-target all` form of the command is displayed.
|
The `no bgp retain route-target all` form of the command is displayed.
|
||||||
|
|
||||||
|
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> soo EXTCOMMUNITY
|
||||||
|
|
||||||
|
Without this command, SoO extended community attribute is configured using
|
||||||
|
an inbound route map that sets the SoO value during the update process.
|
||||||
|
With the introduction of the new BGP per-neighbor Site-of-Origin (SoO) feature,
|
||||||
|
two new commands configured in sub-modes under router configuration mode
|
||||||
|
simplify the SoO value configuration.
|
||||||
|
|
||||||
|
If we configure SoO per neighbor at PEs, the SoO community is automatically
|
||||||
|
added for all routes from the CPEs. Routes are validated and prevented from
|
||||||
|
being sent back to the same CPE (e.g.: multi-site). This is especially needed
|
||||||
|
when using ``as-override`` or ``allowas-in`` to prevent routing loops.
|
||||||
|
|
||||||
.. _bgp-l3vpn-srv6:
|
.. _bgp-l3vpn-srv6:
|
||||||
|
|
||||||
L3VPN SRv6
|
L3VPN SRv6
|
||||||
|
Loading…
Reference in New Issue
Block a user