forked from Mirror/frr
Merge pull request #17871 from opensourcerouting/feature/bgp_link_local_capability
bgpd: Implement Link-Local Next Hop capability
This commit is contained in:
commit
2ef76a3350
|
@ -2450,6 +2450,10 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
|
||||||
if (!peer->nexthop.ifp) {
|
if (!peer->nexthop.ifp) {
|
||||||
zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
|
zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
|
||||||
peer->host);
|
peer->host);
|
||||||
|
if (CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
|
||||||
|
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))
|
||||||
|
bgp_notify_send(peer->connection, BGP_NOTIFY_UPDATE_ERR,
|
||||||
|
BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP);
|
||||||
return BGP_ATTR_PARSE_WITHDRAW;
|
return BGP_ATTR_PARSE_WITHDRAW;
|
||||||
}
|
}
|
||||||
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
|
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
|
||||||
|
|
|
@ -149,6 +149,7 @@ static const struct message bgp_notify_update_msg[] = {
|
||||||
{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
|
{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
|
||||||
{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
|
{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
|
||||||
{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
|
{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
|
||||||
|
{BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP, "/Unreachable Link-Local Address"},
|
||||||
{0}};
|
{0}};
|
||||||
|
|
||||||
static const struct message bgp_notify_cease_msg[] = {
|
static const struct message bgp_notify_cease_msg[] = {
|
||||||
|
|
|
@ -38,7 +38,7 @@ extern struct zclient *zclient;
|
||||||
|
|
||||||
static void register_zebra_rnh(struct bgp_nexthop_cache *bnc);
|
static void register_zebra_rnh(struct bgp_nexthop_cache *bnc);
|
||||||
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
|
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
|
||||||
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
|
static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
|
||||||
static void bgp_nht_ifp_initial(struct event *thread);
|
static void bgp_nht_ifp_initial(struct event *thread);
|
||||||
|
|
||||||
DEFINE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid),
|
DEFINE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid),
|
||||||
|
@ -330,7 +330,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
|
||||||
|
|
||||||
/* This will return true if the global IPv6 NH is a link local
|
/* This will return true if the global IPv6 NH is a link local
|
||||||
* addr */
|
* addr */
|
||||||
if (make_prefix(afi, pi, &p) < 0)
|
if (!make_prefix(afi, pi, &p))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1026,7 +1026,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
|
||||||
* make_prefix - make a prefix structure from the path (essentially
|
* make_prefix - make a prefix structure from the path (essentially
|
||||||
* path's node.
|
* path's node.
|
||||||
*/
|
*/
|
||||||
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||||
{
|
{
|
||||||
|
|
||||||
int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP)
|
int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP)
|
||||||
|
@ -1036,12 +1036,13 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||||
struct bgp_dest *net = pi->net;
|
struct bgp_dest *net = pi->net;
|
||||||
const struct prefix *p_orig = bgp_dest_get_prefix(net);
|
const struct prefix *p_orig = bgp_dest_get_prefix(net);
|
||||||
struct in_addr ipv4;
|
struct in_addr ipv4;
|
||||||
|
struct peer *peer = pi->peer;
|
||||||
|
struct attr *attr = pi->attr;
|
||||||
|
|
||||||
if (p_orig->family == AF_FLOWSPEC) {
|
if (p_orig->family == AF_FLOWSPEC) {
|
||||||
if (!pi->peer)
|
if (!peer)
|
||||||
return -1;
|
return false;
|
||||||
return bgp_flowspec_get_first_nh(pi->peer->bgp,
|
return bgp_flowspec_get_first_nh(peer->bgp, pi, p, afi);
|
||||||
pi, p, afi);
|
|
||||||
}
|
}
|
||||||
memset(p, 0, sizeof(struct prefix));
|
memset(p, 0, sizeof(struct prefix));
|
||||||
switch (afi) {
|
switch (afi) {
|
||||||
|
@ -1051,34 +1052,32 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||||
p->u.prefix4 = p_orig->u.prefix4;
|
p->u.prefix4 = p_orig->u.prefix4;
|
||||||
p->prefixlen = p_orig->prefixlen;
|
p->prefixlen = p_orig->prefixlen;
|
||||||
} else {
|
} else {
|
||||||
if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
|
if (IS_MAPPED_IPV6(&attr->mp_nexthop_global)) {
|
||||||
ipv4_mapped_ipv6_to_ipv4(
|
ipv4_mapped_ipv6_to_ipv4(&attr->mp_nexthop_global, &ipv4);
|
||||||
&pi->attr->mp_nexthop_global, &ipv4);
|
|
||||||
p->u.prefix4 = ipv4;
|
p->u.prefix4 = ipv4;
|
||||||
p->prefixlen = IPV4_MAX_BITLEN;
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||||||
} else {
|
} else {
|
||||||
if (p_orig->family == AF_EVPN)
|
if (p_orig->family == AF_EVPN)
|
||||||
p->u.prefix4 =
|
p->u.prefix4 = attr->mp_nexthop_global_in;
|
||||||
pi->attr->mp_nexthop_global_in;
|
|
||||||
else
|
else
|
||||||
p->u.prefix4 = pi->attr->nexthop;
|
p->u.prefix4 = attr->nexthop;
|
||||||
p->prefixlen = IPV4_MAX_BITLEN;
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AFI_IP6:
|
case AFI_IP6:
|
||||||
p->family = AF_INET6;
|
p->family = AF_INET6;
|
||||||
if (pi->attr->srv6_l3vpn) {
|
if (attr->srv6_l3vpn) {
|
||||||
p->prefixlen = IPV6_MAX_BITLEN;
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||||||
if (pi->attr->srv6_l3vpn->transposition_len != 0 &&
|
if (attr->srv6_l3vpn->transposition_len != 0 &&
|
||||||
BGP_PATH_INFO_NUM_LABELS(pi)) {
|
BGP_PATH_INFO_NUM_LABELS(pi)) {
|
||||||
IPV6_ADDR_COPY(&p->u.prefix6, &pi->attr->srv6_l3vpn->sid);
|
IPV6_ADDR_COPY(&p->u.prefix6, &attr->srv6_l3vpn->sid);
|
||||||
transpose_sid(&p->u.prefix6,
|
transpose_sid(&p->u.prefix6,
|
||||||
decode_label(&pi->extra->labels->label[0]),
|
decode_label(&pi->extra->labels->label[0]),
|
||||||
pi->attr->srv6_l3vpn->transposition_offset,
|
attr->srv6_l3vpn->transposition_offset,
|
||||||
pi->attr->srv6_l3vpn->transposition_len);
|
attr->srv6_l3vpn->transposition_len);
|
||||||
} else
|
} else
|
||||||
IPV6_ADDR_COPY(&(p->u.prefix6), &(pi->attr->srv6_l3vpn->sid));
|
IPV6_ADDR_COPY(&(p->u.prefix6), &(attr->srv6_l3vpn->sid));
|
||||||
} else if (is_bgp_static) {
|
} else if (is_bgp_static) {
|
||||||
p->u.prefix6 = p_orig->u.prefix6;
|
p->u.prefix6 = p_orig->u.prefix6;
|
||||||
p->prefixlen = p_orig->prefixlen;
|
p->prefixlen = p_orig->prefixlen;
|
||||||
|
@ -1086,28 +1085,35 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||||
/* If we receive MP_REACH nexthop with ::(LL)
|
/* If we receive MP_REACH nexthop with ::(LL)
|
||||||
* or LL(LL), use LL address as nexthop cache.
|
* or LL(LL), use LL address as nexthop cache.
|
||||||
*/
|
*/
|
||||||
if (pi->attr &&
|
if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
|
||||||
pi->attr->mp_nexthop_len ==
|
(IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
|
||||||
BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
|
IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)))
|
||||||
(IN6_IS_ADDR_UNSPECIFIED(
|
p->u.prefix6 = attr->mp_nexthop_local;
|
||||||
&pi->attr->mp_nexthop_global) ||
|
|
||||||
IN6_IS_ADDR_LINKLOCAL(&pi->attr->mp_nexthop_global)))
|
|
||||||
p->u.prefix6 = pi->attr->mp_nexthop_local;
|
|
||||||
/* If we receive MR_REACH with (GA)::(LL)
|
/* If we receive MR_REACH with (GA)::(LL)
|
||||||
* then check for route-map to choose GA or LL
|
* then check for route-map to choose GA or LL
|
||||||
*/
|
*/
|
||||||
else if (pi->attr &&
|
else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
|
||||||
pi->attr->mp_nexthop_len ==
|
if (CHECK_FLAG(attr->nh_flags, BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||||
BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
|
p->u.prefix6 = attr->mp_nexthop_global;
|
||||||
if (CHECK_FLAG(pi->attr->nh_flags,
|
|
||||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
|
||||||
p->u.prefix6 =
|
|
||||||
pi->attr->mp_nexthop_global;
|
|
||||||
else
|
else
|
||||||
p->u.prefix6 =
|
p->u.prefix6 = attr->mp_nexthop_local;
|
||||||
pi->attr->mp_nexthop_local;
|
} else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL &&
|
||||||
|
IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
|
||||||
|
/* If we receive MP_REACH with GUA as LL, we should
|
||||||
|
* check if we have Link-Local Next Hop capability also.
|
||||||
|
*/
|
||||||
|
if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
|
||||||
|
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))) {
|
||||||
|
zlog_warn("%s: received IPv6 global next-hop as Link-Local, but no capability exchanged",
|
||||||
|
__func__);
|
||||||
|
p->u.prefix6 = attr->mp_nexthop_global;
|
||||||
|
} else {
|
||||||
|
p->u.prefix6 = attr->mp_nexthop_global;
|
||||||
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
p->u.prefix6 = pi->attr->mp_nexthop_global;
|
p->u.prefix6 = attr->mp_nexthop_global;
|
||||||
p->prefixlen = IPV6_MAX_BITLEN;
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1119,7 +1125,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,6 +43,7 @@ const struct message capcode_str[] = {
|
||||||
{ CAPABILITY_CODE_ROLE, "Role" },
|
{ CAPABILITY_CODE_ROLE, "Role" },
|
||||||
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
|
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
|
||||||
{ CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
|
{ CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
|
||||||
|
{ CAPABILITY_CODE_LINK_LOCAL, "Link-Local Next Hop" },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ const size_t cap_minsizes[] = {
|
||||||
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
|
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
|
||||||
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN,
|
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN,
|
||||||
[CAPABILITY_CODE_PATHS_LIMIT] = CAPABILITY_CODE_PATHS_LIMIT_LEN,
|
[CAPABILITY_CODE_PATHS_LIMIT] = CAPABILITY_CODE_PATHS_LIMIT_LEN,
|
||||||
|
[CAPABILITY_CODE_LINK_LOCAL] = CAPABILITY_CODE_LINK_LOCAL_LEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* value the capability must be a multiple of.
|
/* value the capability must be a multiple of.
|
||||||
|
@ -1067,6 +1069,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
|
||||||
case CAPABILITY_CODE_ROLE:
|
case CAPABILITY_CODE_ROLE:
|
||||||
case CAPABILITY_CODE_SOFT_VERSION:
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
case CAPABILITY_CODE_PATHS_LIMIT:
|
case CAPABILITY_CODE_PATHS_LIMIT:
|
||||||
|
case CAPABILITY_CODE_LINK_LOCAL:
|
||||||
/* Check length. */
|
/* Check length. */
|
||||||
if (caphdr.length < cap_minsizes[caphdr.code]) {
|
if (caphdr.length < cap_minsizes[caphdr.code]) {
|
||||||
zlog_info(
|
zlog_info(
|
||||||
|
@ -1168,6 +1171,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
|
||||||
case CAPABILITY_CODE_SOFT_VERSION:
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
ret = bgp_capability_software_version(peer, &caphdr);
|
ret = bgp_capability_software_version(peer, &caphdr);
|
||||||
break;
|
break;
|
||||||
|
case CAPABILITY_CODE_LINK_LOCAL:
|
||||||
|
SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV);
|
||||||
|
break;
|
||||||
case CAPABILITY_CODE_PATHS_LIMIT:
|
case CAPABILITY_CODE_PATHS_LIMIT:
|
||||||
ret = bgp_capability_paths_limit(peer, &caphdr);
|
ret = bgp_capability_paths_limit(peer, &caphdr);
|
||||||
break;
|
break;
|
||||||
|
@ -1968,6 +1974,16 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
|
||||||
stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
|
stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Link-Local Next Hop capability. */
|
||||||
|
if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL)) {
|
||||||
|
SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV);
|
||||||
|
stream_putc(s, BGP_OPEN_OPT_CAP);
|
||||||
|
ext_opt_params ? stream_putw(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2)
|
||||||
|
: stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2);
|
||||||
|
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
|
||||||
|
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
/* FQDN capability */
|
/* FQDN capability */
|
||||||
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN)
|
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN)
|
||||||
&& cmd_hostname_get()) {
|
&& cmd_hostname_get()) {
|
||||||
|
|
|
@ -54,6 +54,7 @@ struct graceful_restart_af {
|
||||||
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
|
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
|
||||||
#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
|
#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
|
||||||
#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */
|
#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */
|
||||||
|
#define CAPABILITY_CODE_LINK_LOCAL 77 /* draft-white-linklocal-capability */
|
||||||
|
|
||||||
/* Capability Length */
|
/* Capability Length */
|
||||||
#define CAPABILITY_CODE_MP_LEN 4
|
#define CAPABILITY_CODE_MP_LEN 4
|
||||||
|
@ -71,6 +72,7 @@ struct graceful_restart_af {
|
||||||
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
|
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
|
||||||
#define CAPABILITY_CODE_ROLE_LEN 1
|
#define CAPABILITY_CODE_ROLE_LEN 1
|
||||||
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
|
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
|
||||||
|
#define CAPABILITY_CODE_LINK_LOCAL_LEN 0
|
||||||
|
|
||||||
/* Cooperative Route Filtering Capability. */
|
/* Cooperative Route Filtering Capability. */
|
||||||
|
|
||||||
|
|
|
@ -1245,6 +1245,18 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
|
||||||
|
|
||||||
/* Encode MP_EXT capability. */
|
/* Encode MP_EXT capability. */
|
||||||
switch (capability_code) {
|
switch (capability_code) {
|
||||||
|
case CAPABILITY_CODE_LINK_LOCAL:
|
||||||
|
stream_putc(s, action);
|
||||||
|
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
|
||||||
|
stream_putc(s, 0);
|
||||||
|
|
||||||
|
if (bgp_debug_neighbor_events(peer))
|
||||||
|
zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", peer,
|
||||||
|
action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing",
|
||||||
|
capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
|
||||||
|
|
||||||
|
COND_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV, action == CAPABILITY_ACTION_SET);
|
||||||
|
break;
|
||||||
case CAPABILITY_CODE_SOFT_VERSION:
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
stream_putc(s, action);
|
stream_putc(s, action);
|
||||||
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
|
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
|
||||||
|
@ -3769,6 +3781,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
|
||||||
case CAPABILITY_CODE_ROLE:
|
case CAPABILITY_CODE_ROLE:
|
||||||
case CAPABILITY_CODE_SOFT_VERSION:
|
case CAPABILITY_CODE_SOFT_VERSION:
|
||||||
case CAPABILITY_CODE_PATHS_LIMIT:
|
case CAPABILITY_CODE_PATHS_LIMIT:
|
||||||
|
case CAPABILITY_CODE_LINK_LOCAL:
|
||||||
if (hdr->length < cap_minsizes[hdr->code]) {
|
if (hdr->length < cap_minsizes[hdr->code]) {
|
||||||
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
|
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
|
||||||
peer, capability, hdr->length,
|
peer, capability, hdr->length,
|
||||||
|
|
|
@ -2506,8 +2506,16 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
|
||||||
} else if (!ibgp_to_ibgp && !transparent &&
|
} else if (!ibgp_to_ibgp && !transparent &&
|
||||||
!CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) &&
|
!CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) &&
|
||||||
IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network &&
|
IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network &&
|
||||||
(from == bgp->peer_self || peer->sort == BGP_PEER_EBGP))
|
(from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)) {
|
||||||
global_and_ll = true;
|
/* If an implementation intends to send a single link-local forwarding
|
||||||
|
* address in the Next Hop field of the MP_REACH_NLRI, it MUST set the
|
||||||
|
* length of the Next Hop field to 16 and include only the IPv6 link-local
|
||||||
|
* address in the Next Hop field.
|
||||||
|
*/
|
||||||
|
if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
|
||||||
|
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV)))
|
||||||
|
global_and_ll = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (global_and_ll) {
|
if (global_and_ll) {
|
||||||
if (safi == SAFI_MPLS_VPN)
|
if (safi == SAFI_MPLS_VPN)
|
||||||
|
@ -9946,6 +9954,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
|
||||||
"ipv6");
|
"ipv6");
|
||||||
json_object_string_add(json_nexthop_global, "scope",
|
json_object_string_add(json_nexthop_global, "scope",
|
||||||
"global");
|
"global");
|
||||||
|
json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
|
||||||
|
|
||||||
/* We display both LL & GL if both have been
|
/* We display both LL & GL if both have been
|
||||||
* received */
|
* received */
|
||||||
|
@ -9969,6 +9978,8 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
|
||||||
"ipv6");
|
"ipv6");
|
||||||
json_object_string_add(json_nexthop_ll, "scope",
|
json_object_string_add(json_nexthop_ll, "scope",
|
||||||
"link-local");
|
"link-local");
|
||||||
|
json_object_int_add(json_nexthop_global, "length",
|
||||||
|
attr->mp_nexthop_len);
|
||||||
|
|
||||||
if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
|
if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
|
||||||
&attr->mp_nexthop_local) !=
|
&attr->mp_nexthop_local) !=
|
||||||
|
@ -11090,6 +11101,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
|
||||||
"ipv6");
|
"ipv6");
|
||||||
json_object_string_add(json_nexthop_global, "scope",
|
json_object_string_add(json_nexthop_global, "scope",
|
||||||
"global");
|
"global");
|
||||||
|
json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
|
||||||
} else {
|
} else {
|
||||||
if (nexthop_hostname)
|
if (nexthop_hostname)
|
||||||
vty_out(vty, " %pI6(%s)",
|
vty_out(vty, " %pI6(%s)",
|
||||||
|
@ -11277,6 +11289,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
|
||||||
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
|
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
|
||||||
json_object_string_add(json_nexthop_ll, "scope",
|
json_object_string_add(json_nexthop_ll, "scope",
|
||||||
"link-local");
|
"link-local");
|
||||||
|
json_object_int_add(json_nexthop_ll, "length", attr->mp_nexthop_len);
|
||||||
|
|
||||||
json_object_boolean_true_add(json_nexthop_ll,
|
json_object_boolean_true_add(json_nexthop_ll,
|
||||||
"accessible");
|
"accessible");
|
||||||
|
|
|
@ -114,6 +114,10 @@ FRR_CFG_DEFAULT_BOOL(BGP_SOFT_VERSION_CAPABILITY,
|
||||||
{ .val_bool = true, .match_profile = "datacenter", },
|
{ .val_bool = true, .match_profile = "datacenter", },
|
||||||
{ .val_bool = false },
|
{ .val_bool = false },
|
||||||
);
|
);
|
||||||
|
FRR_CFG_DEFAULT_BOOL(BGP_LINK_LOCAL_CAPABILITY,
|
||||||
|
{ .val_bool = true, .match_profile = "datacenter", },
|
||||||
|
{ .val_bool = false },
|
||||||
|
);
|
||||||
FRR_CFG_DEFAULT_BOOL(BGP_DYNAMIC_CAPABILITY,
|
FRR_CFG_DEFAULT_BOOL(BGP_DYNAMIC_CAPABILITY,
|
||||||
{ .val_bool = true, .match_profile = "datacenter", },
|
{ .val_bool = true, .match_profile = "datacenter", },
|
||||||
{ .val_bool = false },
|
{ .val_bool = false },
|
||||||
|
@ -623,6 +627,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
|
||||||
if (DFLT_BGP_SOFT_VERSION_CAPABILITY)
|
if (DFLT_BGP_SOFT_VERSION_CAPABILITY)
|
||||||
SET_FLAG((*bgp)->flags,
|
SET_FLAG((*bgp)->flags,
|
||||||
BGP_FLAG_SOFT_VERSION_CAPABILITY);
|
BGP_FLAG_SOFT_VERSION_CAPABILITY);
|
||||||
|
if (DFLT_BGP_LINK_LOCAL_CAPABILITY)
|
||||||
|
SET_FLAG((*bgp)->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
|
||||||
if (DFLT_BGP_DYNAMIC_CAPABILITY)
|
if (DFLT_BGP_DYNAMIC_CAPABILITY)
|
||||||
SET_FLAG((*bgp)->flags,
|
SET_FLAG((*bgp)->flags,
|
||||||
BGP_FLAG_DYNAMIC_CAPABILITY);
|
BGP_FLAG_DYNAMIC_CAPABILITY);
|
||||||
|
@ -4436,6 +4442,24 @@ DEFPY (bgp_default_software_version_capability,
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFPY (bgp_default_link_local_capability,
|
||||||
|
bgp_default_link_local_capability_cmd,
|
||||||
|
"[no] bgp default link-local-capability",
|
||||||
|
NO_STR
|
||||||
|
BGP_STR
|
||||||
|
"Configure BGP defaults\n"
|
||||||
|
"Advertise Link-Local Next Hop capability for all neighbors\n")
|
||||||
|
{
|
||||||
|
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
||||||
|
|
||||||
|
if (no)
|
||||||
|
UNSET_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
|
||||||
|
else
|
||||||
|
SET_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFPY (bgp_default_dynamic_capability,
|
DEFPY (bgp_default_dynamic_capability,
|
||||||
bgp_default_dynamic_capability_cmd,
|
bgp_default_dynamic_capability_cmd,
|
||||||
"[no] bgp default dynamic-capability",
|
"[no] bgp default dynamic-capability",
|
||||||
|
@ -6065,6 +6089,34 @@ DEFPY(neighbor_capability_software_version,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* neighbor capability link-local */
|
||||||
|
DEFPY(neighbor_capability_link_local,
|
||||||
|
neighbor_capability_link_local_cmd,
|
||||||
|
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor capability link-local",
|
||||||
|
NO_STR
|
||||||
|
NEIGHBOR_STR
|
||||||
|
NEIGHBOR_ADDR_STR2
|
||||||
|
"Advertise capability to the peer\n"
|
||||||
|
"Advertise Link-Local Next Hop capability to the peer\n")
|
||||||
|
{
|
||||||
|
struct peer *peer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
peer = peer_and_group_lookup_vty(vty, neighbor);
|
||||||
|
if (!peer)
|
||||||
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
|
|
||||||
|
if (no)
|
||||||
|
ret = peer_flag_unset_vty(vty, neighbor, PEER_FLAG_CAPABILITY_LINK_LOCAL);
|
||||||
|
else
|
||||||
|
ret = peer_flag_set_vty(vty, neighbor, PEER_FLAG_CAPABILITY_LINK_LOCAL);
|
||||||
|
|
||||||
|
bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, CAPABILITY_CODE_LINK_LOCAL,
|
||||||
|
no ? CAPABILITY_ACTION_UNSET : CAPABILITY_ACTION_SET);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
|
static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
|
||||||
afi_t afi, safi_t safi, uint64_t flag,
|
afi_t afi, safi_t safi, uint64_t flag,
|
||||||
int set)
|
int set)
|
||||||
|
@ -14942,6 +14994,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
|
||||||
json_object_object_add(json_cap, "softwareVersion",
|
json_object_object_add(json_cap, "softwareVersion",
|
||||||
json_soft_version);
|
json_soft_version);
|
||||||
|
|
||||||
|
/* Link-Local Next Hop capability */
|
||||||
|
json_object *json_link_local = NULL;
|
||||||
|
|
||||||
|
json_link_local = json_object_new_object();
|
||||||
|
json_object_boolean_add(json_link_local, "advertised",
|
||||||
|
!!CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_ADV));
|
||||||
|
json_object_boolean_add(json_link_local, "received",
|
||||||
|
!!CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_RCV));
|
||||||
|
json_object_object_add(json_cap, "linkLocalNextHop", json_link_local);
|
||||||
|
|
||||||
/* Graceful Restart */
|
/* Graceful Restart */
|
||||||
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
||||||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
||||||
|
@ -15369,6 +15431,21 @@ CPP_NOTICE("Remove `gracefulRestartCapability` JSON field")
|
||||||
|
|
||||||
vty_out(vty, "\n");
|
vty_out(vty, "\n");
|
||||||
|
|
||||||
|
/* Link-Local Next Hop capability */
|
||||||
|
vty_out(vty, " Link-Local Next Hop Capability:");
|
||||||
|
|
||||||
|
if (CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_ADV))
|
||||||
|
vty_out(vty, " advertised link-local");
|
||||||
|
else
|
||||||
|
vty_out(vty, " not advertised");
|
||||||
|
|
||||||
|
if (CHECK_FLAG(p->cap, PEER_CAP_LINK_LOCAL_RCV))
|
||||||
|
vty_out(vty, " received link-local");
|
||||||
|
else
|
||||||
|
vty_out(vty, " not received");
|
||||||
|
|
||||||
|
vty_out(vty, "\n");
|
||||||
|
|
||||||
/* Graceful Restart */
|
/* Graceful Restart */
|
||||||
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
|
||||||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
|
||||||
|
@ -18913,6 +18990,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
|
||||||
addr);
|
addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* capability link-local */
|
||||||
|
if (CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY)) {
|
||||||
|
if (!peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL))
|
||||||
|
vty_out(vty, " no neighbor %s capability link-local\n", addr);
|
||||||
|
} else {
|
||||||
|
if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL))
|
||||||
|
vty_out(vty, " neighbor %s capability link-local\n", addr);
|
||||||
|
}
|
||||||
|
|
||||||
/* dont-capability-negotiation */
|
/* dont-capability-negotiation */
|
||||||
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
|
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
|
||||||
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
|
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
|
||||||
|
@ -19622,6 +19708,11 @@ int bgp_config_write(struct vty *vty)
|
||||||
? ""
|
? ""
|
||||||
: "no ");
|
: "no ");
|
||||||
|
|
||||||
|
if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY) !=
|
||||||
|
SAVE_BGP_LINK_LOCAL_CAPABILITY)
|
||||||
|
vty_out(vty, " %sbgp default link-local-capability\n",
|
||||||
|
CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY) ? "" : "no ");
|
||||||
|
|
||||||
if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY) !=
|
if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY) !=
|
||||||
SAVE_BGP_DYNAMIC_CAPABILITY)
|
SAVE_BGP_DYNAMIC_CAPABILITY)
|
||||||
vty_out(vty,
|
vty_out(vty,
|
||||||
|
@ -20728,6 +20819,9 @@ void bgp_vty_init(void)
|
||||||
/* bgp default software-version-capability */
|
/* bgp default software-version-capability */
|
||||||
install_element(BGP_NODE, &bgp_default_software_version_capability_cmd);
|
install_element(BGP_NODE, &bgp_default_software_version_capability_cmd);
|
||||||
|
|
||||||
|
/* bgp default link-local-capability */
|
||||||
|
install_element(BGP_NODE, &bgp_default_link_local_capability_cmd);
|
||||||
|
|
||||||
/* bgp default dynamic-capability */
|
/* bgp default dynamic-capability */
|
||||||
install_element(BGP_NODE, &bgp_default_dynamic_capability_cmd);
|
install_element(BGP_NODE, &bgp_default_dynamic_capability_cmd);
|
||||||
|
|
||||||
|
@ -21383,6 +21477,9 @@ void bgp_vty_init(void)
|
||||||
/* "neighbor capability software-version" commands.*/
|
/* "neighbor capability software-version" commands.*/
|
||||||
install_element(BGP_NODE, &neighbor_capability_software_version_cmd);
|
install_element(BGP_NODE, &neighbor_capability_software_version_cmd);
|
||||||
|
|
||||||
|
/* "neighbor capability link-local" commands.*/
|
||||||
|
install_element(BGP_NODE, &neighbor_capability_link_local_cmd);
|
||||||
|
|
||||||
/* "neighbor capability orf prefix-list" commands.*/
|
/* "neighbor capability orf prefix-list" commands.*/
|
||||||
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
|
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
|
||||||
install_element(BGP_NODE,
|
install_element(BGP_NODE,
|
||||||
|
|
14
bgpd/bgpd.c
14
bgpd/bgpd.c
|
@ -1575,6 +1575,9 @@ struct peer *peer_new(struct bgp *bgp)
|
||||||
if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY))
|
if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY))
|
||||||
peer_flag_set(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION);
|
peer_flag_set(peer, PEER_FLAG_CAPABILITY_SOFT_VERSION);
|
||||||
|
|
||||||
|
if (CHECK_FLAG(bgp->flags, BGP_FLAG_LINK_LOCAL_CAPABILITY))
|
||||||
|
peer_flag_set(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL);
|
||||||
|
|
||||||
if (CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY))
|
if (CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY))
|
||||||
peer_flag_set(peer, PEER_FLAG_DYNAMIC_CAPABILITY);
|
peer_flag_set(peer, PEER_FLAG_DYNAMIC_CAPABILITY);
|
||||||
|
|
||||||
|
@ -2964,6 +2967,11 @@ static void peer_group2peer_config_copy(struct peer_group *group,
|
||||||
SET_FLAG(peer->flags,
|
SET_FLAG(peer->flags,
|
||||||
PEER_FLAG_DYNAMIC_CAPABILITY);
|
PEER_FLAG_DYNAMIC_CAPABILITY);
|
||||||
|
|
||||||
|
/* capability link-local apply */
|
||||||
|
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_LINK_LOCAL))
|
||||||
|
if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_LINK_LOCAL))
|
||||||
|
SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_LINK_LOCAL);
|
||||||
|
|
||||||
/* password apply */
|
/* password apply */
|
||||||
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
|
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
|
||||||
PEER_STR_ATTR_INHERIT(peer, group, password,
|
PEER_STR_ATTR_INHERIT(peer, group, password,
|
||||||
|
@ -4834,6 +4842,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
|
||||||
{PEER_FLAG_EXTENDED_LINK_BANDWIDTH, 0, peer_change_none},
|
{PEER_FLAG_EXTENDED_LINK_BANDWIDTH, 0, peer_change_none},
|
||||||
{PEER_FLAG_LONESOUL, 0, peer_change_reset_out},
|
{PEER_FLAG_LONESOUL, 0, peer_change_reset_out},
|
||||||
{PEER_FLAG_TCP_MSS, 0, peer_change_none},
|
{PEER_FLAG_TCP_MSS, 0, peer_change_none},
|
||||||
|
{PEER_FLAG_CAPABILITY_LINK_LOCAL, 0, peer_change_none},
|
||||||
{0, 0, 0}};
|
{0, 0, 0}};
|
||||||
|
|
||||||
static const struct peer_flag_action peer_af_flag_action_list[] = {
|
static const struct peer_flag_action peer_af_flag_action_list[] = {
|
||||||
|
@ -4921,7 +4930,10 @@ static int peer_flag_action_set(const struct peer_flag_action *action_list,
|
||||||
|
|
||||||
static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
|
static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
|
||||||
{
|
{
|
||||||
if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
|
if (flag == PEER_FLAG_DYNAMIC_CAPABILITY || flag == PEER_FLAG_CAPABILITY_ENHE ||
|
||||||
|
flag == PEER_FLAG_CAPABILITY_FQDN || flag == PEER_FLAG_CAPABILITY_SOFT_VERSION ||
|
||||||
|
flag == PEER_FLAG_DONT_CAPABILITY || flag == PEER_FLAG_OVERRIDE_CAPABILITY ||
|
||||||
|
flag == PEER_FLAG_STRICT_CAP_MATCH || flag == PEER_FLAG_CAPABILITY_LINK_LOCAL)
|
||||||
peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
|
peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
|
||||||
else if (flag == PEER_FLAG_PASSIVE)
|
else if (flag == PEER_FLAG_PASSIVE)
|
||||||
peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
|
peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
|
||||||
|
|
|
@ -566,6 +566,7 @@ struct bgp {
|
||||||
#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40)
|
#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40)
|
||||||
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_INSTALL (1ULL << 41)
|
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_INSTALL (1ULL << 41)
|
||||||
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_DELETE (1ULL << 42)
|
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_DELETE (1ULL << 42)
|
||||||
|
#define BGP_FLAG_LINK_LOCAL_CAPABILITY (1ULL << 43)
|
||||||
|
|
||||||
/* BGP default address-families.
|
/* BGP default address-families.
|
||||||
* New peers inherit enabled afi/safis from bgp instance.
|
* New peers inherit enabled afi/safis from bgp instance.
|
||||||
|
@ -1410,6 +1411,8 @@ struct peer {
|
||||||
#define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28)
|
#define PEER_CAP_SOFT_VERSION_RCV (1ULL << 28)
|
||||||
#define PEER_CAP_PATHS_LIMIT_ADV (1U << 29)
|
#define PEER_CAP_PATHS_LIMIT_ADV (1U << 29)
|
||||||
#define PEER_CAP_PATHS_LIMIT_RCV (1U << 30)
|
#define PEER_CAP_PATHS_LIMIT_RCV (1U << 30)
|
||||||
|
#define PEER_CAP_LINK_LOCAL_ADV (1ULL << 31)
|
||||||
|
#define PEER_CAP_LINK_LOCAL_RCV (1ULL << 32)
|
||||||
|
|
||||||
/* Capability flags (reset in bgp_stop) */
|
/* Capability flags (reset in bgp_stop) */
|
||||||
uint32_t af_cap[AFI_MAX][SAFI_MAX];
|
uint32_t af_cap[AFI_MAX][SAFI_MAX];
|
||||||
|
@ -1543,6 +1546,7 @@ struct peer {
|
||||||
#define PEER_FLAG_AS_LOOP_DETECTION (1ULL << 38) /* as path loop detection */
|
#define PEER_FLAG_AS_LOOP_DETECTION (1ULL << 38) /* as path loop detection */
|
||||||
#define PEER_FLAG_EXTENDED_LINK_BANDWIDTH (1ULL << 39)
|
#define PEER_FLAG_EXTENDED_LINK_BANDWIDTH (1ULL << 39)
|
||||||
#define PEER_FLAG_DUAL_AS (1ULL << 40)
|
#define PEER_FLAG_DUAL_AS (1ULL << 40)
|
||||||
|
#define PEER_FLAG_CAPABILITY_LINK_LOCAL (1ULL << 41)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
|
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
|
||||||
|
@ -2100,6 +2104,7 @@ struct bgp_nlri {
|
||||||
#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
|
#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
|
||||||
#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
|
#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
|
||||||
#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
|
#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
|
||||||
|
#define BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP 12 /* draft-white-linklocal-capability */
|
||||||
|
|
||||||
/* BGP_NOTIFY_CEASE sub codes (RFC 4486). */
|
/* BGP_NOTIFY_CEASE sub codes (RFC 4486). */
|
||||||
#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
|
#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
|
||||||
|
|
|
@ -1953,6 +1953,15 @@ Configuring Peers
|
||||||
are not supporting this capability or supporting BGP Capabilities
|
are not supporting this capability or supporting BGP Capabilities
|
||||||
Negotiation RFC 2842.
|
Negotiation RFC 2842.
|
||||||
|
|
||||||
|
.. clicmd:: neighbor PEER capability link-local
|
||||||
|
|
||||||
|
Send the Link-Local Next Hop capability in the BGP OPEN message to the neighbor.
|
||||||
|
This is useful in data center environments where point-to-point (unnumbered) links
|
||||||
|
are utilized. This capability standardizes the operation of BGP over a
|
||||||
|
point-to-point links using link-local IPv6 addressing only.
|
||||||
|
|
||||||
|
Enabled by default for the ``datacenter`` profile.
|
||||||
|
|
||||||
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
|
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
|
||||||
|
|
||||||
Enable handling of self-originated VPN routes containing ``accept-own`` community.
|
Enable handling of self-originated VPN routes containing ``accept-own`` community.
|
||||||
|
|
15
tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf
Normal file
15
tests/topotests/bgp_ipv6_link_local_capability/r1/frr.conf
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
!
|
||||||
|
int lo
|
||||||
|
ip address 10.0.0.1/32
|
||||||
|
!
|
||||||
|
router bgp 65001
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
no bgp network import-check
|
||||||
|
bgp default link-local-capability
|
||||||
|
neighbor r1-eth0 interface remote-as auto
|
||||||
|
address-family ipv6 unicast
|
||||||
|
network 2001:db8::1/128
|
||||||
|
neighbor r1-eth0 activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
!
|
13
tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf
Normal file
13
tests/topotests/bgp_ipv6_link_local_capability/r2/frr.conf
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
!
|
||||||
|
int lo
|
||||||
|
ip address 10.0.0.2/32
|
||||||
|
!
|
||||||
|
router bgp 65002
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
bgp default link-local-capability
|
||||||
|
neighbor r2-eth0 interface remote-as auto
|
||||||
|
address-family ipv6 unicast
|
||||||
|
neighbor r2-eth0 activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
!
|
|
@ -0,0 +1,110 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: ISC
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 by
|
||||||
|
# Donatas Abraitis <donatas@opensourcerouting.org>
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import pytest
|
||||||
|
import functools
|
||||||
|
|
||||||
|
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
sys.path.append(os.path.join(CWD, "../"))
|
||||||
|
|
||||||
|
# pylint: disable=C0413
|
||||||
|
from lib import topotest
|
||||||
|
from lib.topogen import Topogen, get_topogen
|
||||||
|
from lib.common_config import step
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.bgpd]
|
||||||
|
|
||||||
|
|
||||||
|
def build_topo(tgen):
|
||||||
|
tgen.add_router("r1")
|
||||||
|
tgen.add_router("r2")
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(tgen.gears["r1"])
|
||||||
|
switch.add_link(tgen.gears["r2"])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
tgen = Topogen(build_topo, mod.__name__)
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
router_list = tgen.routers()
|
||||||
|
|
||||||
|
for _, (rname, router) in enumerate(router_list.items(), 1):
|
||||||
|
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
|
||||||
|
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(mod):
|
||||||
|
tgen = get_topogen()
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def test_bgp_ipv6_link_local_capability():
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
r2 = tgen.gears["r2"]
|
||||||
|
|
||||||
|
def _bgp_converge():
|
||||||
|
output = json.loads(r2.vtysh_cmd("show bgp neighbor json"))
|
||||||
|
expected = {
|
||||||
|
"r2-eth0": {
|
||||||
|
"neighborCapabilities": {
|
||||||
|
"linkLocalNextHop": {
|
||||||
|
"advertised": True,
|
||||||
|
"received": True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
test_func = functools.partial(_bgp_converge)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||||
|
assert result is None, "Can't converge initially"
|
||||||
|
|
||||||
|
def _bgp_check_received_nexthops():
|
||||||
|
output = json.loads(r2.vtysh_cmd("show bgp 2001:db8::1/128 json"))
|
||||||
|
expected = {
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"valid": True,
|
||||||
|
"nexthops": [
|
||||||
|
{
|
||||||
|
"hostname": "r1",
|
||||||
|
"afi": "ipv6",
|
||||||
|
"scope": "global",
|
||||||
|
"length": 16,
|
||||||
|
"accessible": True,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"peer": {
|
||||||
|
"routerId": "10.0.0.1",
|
||||||
|
"hostname": "r1",
|
||||||
|
"interface": "r2-eth0",
|
||||||
|
"type": "external",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
test_func = functools.partial(_bgp_check_received_nexthops)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||||
|
assert result is None, "Can't see 2001:db8::1/128"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in a new issue