mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 04:26:12 +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->orf_plist[afi][safi] = from_peer->orf_plist[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) {
|
||||
|
@ -2316,6 +2316,29 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
|
||||
if (aspath_check_as_sets(attr->aspath))
|
||||
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 */
|
||||
if (aspath_check_as_zero(attr->aspath))
|
||||
return false;
|
||||
@ -4057,6 +4080,30 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
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);
|
||||
|
||||
/* 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->shared_network = src->shared_network;
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
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)) {
|
||||
zlog_debug(
|
||||
"%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"
|
||||
"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" */
|
||||
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 */
|
||||
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
|
||||
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, &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. */
|
||||
install_element(BGP_NODE, &address_family_ipv4_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],
|
||||
PEER_FLAG_SEND_LARGE_COMMUNITY);
|
||||
peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
|
||||
peer->soo[afi][safi] = NULL;
|
||||
}
|
||||
|
||||
/* 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->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++) {
|
||||
@ -2042,6 +2048,10 @@ static void peer_group2peer_config_copy_af(struct peer_group *group,
|
||||
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
|
||||
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 */
|
||||
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_WEIGHT))
|
||||
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_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name);
|
||||
ecommunity_free(&peer->soo[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_WEIGHT, 0, peer_change_reset_in},
|
||||
{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
|
||||
{PEER_FLAG_SOO, 0, peer_change_reset},
|
||||
{0, 0, 0}};
|
||||
|
||||
/* 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_FORCE (1U << 28) /* maximum-prefix <num> force */
|
||||
#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];
|
||||
|
||||
@ -1620,6 +1621,9 @@ struct peer {
|
||||
/* allowas-in. */
|
||||
char allowas_in[AFI_MAX][SAFI_MAX];
|
||||
|
||||
/* soo */
|
||||
struct ecommunity *soo[AFI_MAX][SAFI_MAX];
|
||||
|
||||
/* weight */
|
||||
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.
|
||||
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:
|
||||
|
||||
L3VPN SRv6
|
||||
|
Loading…
Reference in New Issue
Block a user