summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c4
-rw-r--r--bgpd/bgp_debug.c1
-rw-r--r--bgpd/bgp_nht.c80
-rw-r--r--bgpd/bgp_open.c16
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_packet.c13
-rw-r--r--bgpd/bgp_route.c12
-rw-r--r--bgpd/bgp_vty.c97
-rw-r--r--bgpd/bgpd.c11
-rw-r--r--bgpd/bgpd.h5
10 files changed, 201 insertions, 40 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index d349922c52..c15dada9c1 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2450,6 +2450,10 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
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",
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;
}
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 319638e412..dfebc00e0a 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -149,6 +149,7 @@ static const struct message bgp_notify_update_msg[] = {
{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
+ {BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP, "/Unreachable Link-Local Address"},
{0}};
static const struct message bgp_notify_cease_msg[] = {
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 164e2300c0..37c4021890 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -38,7 +38,7 @@ extern struct zclient *zclient;
static void register_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);
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
* addr */
- if (make_prefix(afi, pi, &p) < 0)
+ if (!make_prefix(afi, pi, &p))
return 1;
/*
@@ -1026,7 +1026,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
* make_prefix - make a prefix structure from the path (essentially
* 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)
@@ -1036,12 +1036,13 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
struct bgp_dest *net = pi->net;
const struct prefix *p_orig = bgp_dest_get_prefix(net);
struct in_addr ipv4;
+ struct peer *peer = pi->peer;
+ struct attr *attr = pi->attr;
if (p_orig->family == AF_FLOWSPEC) {
- if (!pi->peer)
- return -1;
- return bgp_flowspec_get_first_nh(pi->peer->bgp,
- pi, p, afi);
+ if (!peer)
+ return false;
+ return bgp_flowspec_get_first_nh(peer->bgp, pi, p, afi);
}
memset(p, 0, sizeof(struct prefix));
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->prefixlen = p_orig->prefixlen;
} else {
- if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
- ipv4_mapped_ipv6_to_ipv4(
- &pi->attr->mp_nexthop_global, &ipv4);
+ if (IS_MAPPED_IPV6(&attr->mp_nexthop_global)) {
+ ipv4_mapped_ipv6_to_ipv4(&attr->mp_nexthop_global, &ipv4);
p->u.prefix4 = ipv4;
p->prefixlen = IPV4_MAX_BITLEN;
} else {
if (p_orig->family == AF_EVPN)
- p->u.prefix4 =
- pi->attr->mp_nexthop_global_in;
+ p->u.prefix4 = attr->mp_nexthop_global_in;
else
- p->u.prefix4 = pi->attr->nexthop;
+ p->u.prefix4 = attr->nexthop;
p->prefixlen = IPV4_MAX_BITLEN;
}
}
break;
case AFI_IP6:
p->family = AF_INET6;
- if (pi->attr->srv6_l3vpn) {
+ if (attr->srv6_l3vpn) {
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)) {
- 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,
decode_label(&pi->extra->labels->label[0]),
- pi->attr->srv6_l3vpn->transposition_offset,
- pi->attr->srv6_l3vpn->transposition_len);
+ attr->srv6_l3vpn->transposition_offset,
+ attr->srv6_l3vpn->transposition_len);
} 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) {
p->u.prefix6 = p_orig->u.prefix6;
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)
* or LL(LL), use LL address as nexthop cache.
*/
- if (pi->attr &&
- pi->attr->mp_nexthop_len ==
- BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
- (IN6_IS_ADDR_UNSPECIFIED(
- &pi->attr->mp_nexthop_global) ||
- IN6_IS_ADDR_LINKLOCAL(&pi->attr->mp_nexthop_global)))
- p->u.prefix6 = pi->attr->mp_nexthop_local;
+ if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
+ (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
+ IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)))
+ p->u.prefix6 = attr->mp_nexthop_local;
/* If we receive MR_REACH with (GA)::(LL)
* then check for route-map to choose GA or LL
*/
- else if (pi->attr &&
- pi->attr->mp_nexthop_len ==
- BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
- if (CHECK_FLAG(pi->attr->nh_flags,
- BGP_ATTR_NH_MP_PREFER_GLOBAL))
- p->u.prefix6 =
- pi->attr->mp_nexthop_global;
+ else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ if (CHECK_FLAG(attr->nh_flags, BGP_ATTR_NH_MP_PREFER_GLOBAL))
+ p->u.prefix6 = attr->mp_nexthop_global;
else
- p->u.prefix6 =
- pi->attr->mp_nexthop_local;
+ p->u.prefix6 = 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
- p->u.prefix6 = pi->attr->mp_nexthop_global;
+ p->u.prefix6 = attr->mp_nexthop_global;
p->prefixlen = IPV6_MAX_BITLEN;
}
break;
@@ -1119,7 +1125,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
}
break;
}
- return 0;
+ return true;
}
/**
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 6451c7cf38..be04d87b74 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -43,6 +43,7 @@ const struct message capcode_str[] = {
{ CAPABILITY_CODE_ROLE, "Role" },
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
{ CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
+ { CAPABILITY_CODE_LINK_LOCAL, "Link-Local Next Hop" },
{ 0 }
};
@@ -63,6 +64,7 @@ const size_t cap_minsizes[] = {
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_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.
@@ -1067,6 +1069,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
+ case CAPABILITY_CODE_LINK_LOCAL:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -1168,6 +1171,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_SOFT_VERSION:
ret = bgp_capability_software_version(peer, &caphdr);
break;
+ case CAPABILITY_CODE_LINK_LOCAL:
+ SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV);
+ break;
case CAPABILITY_CODE_PATHS_LIMIT:
ret = bgp_capability_paths_limit(peer, &caphdr);
break;
@@ -1968,6 +1974,16 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
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 */
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN)
&& cmd_hostname_get()) {
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 3a8cba9b7d..abe3b51f5d 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -54,6 +54,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */
+#define CAPABILITY_CODE_LINK_LOCAL 77 /* draft-white-linklocal-capability */
/* Capability Length */
#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_ROLE_LEN 1
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
+#define CAPABILITY_CODE_LINK_LOCAL_LEN 0
/* Cooperative Route Filtering Capability. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index ca2e8de041..ca0d414dbb 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1243,6 +1243,18 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
/* Encode MP_EXT capability. */
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:
stream_putc(s, action);
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
@@ -3943,6 +3955,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
+ case CAPABILITY_CODE_LINK_LOCAL:
if (hdr->length < cap_minsizes[hdr->code]) {
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
peer, capability, hdr->length,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 19d246bdcb..09420f2f6b 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2493,8 +2493,16 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
} else if (!ibgp_to_ibgp && !transparent &&
!CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) &&
IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network &&
- (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP))
- global_and_ll = true;
+ (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)) {
+ /* 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 (safi == SAFI_MPLS_VPN)
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 33b220d3ec..0c5d0b10b0 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -114,6 +114,10 @@ FRR_CFG_DEFAULT_BOOL(BGP_SOFT_VERSION_CAPABILITY,
{ .val_bool = true, .match_profile = "datacenter", },
{ .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,
{ .val_bool = true, .match_profile = "datacenter", },
{ .val_bool = false },
@@ -617,6 +621,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
if (DFLT_BGP_SOFT_VERSION_CAPABILITY)
SET_FLAG((*bgp)->flags,
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)
SET_FLAG((*bgp)->flags,
BGP_FLAG_DYNAMIC_CAPABILITY);
@@ -4422,6 +4428,24 @@ DEFPY (bgp_default_software_version_capability,
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,
bgp_default_dynamic_capability_cmd,
"[no] bgp default dynamic-capability",
@@ -6051,6 +6075,34 @@ DEFPY(neighbor_capability_software_version,
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,
afi_t afi, safi_t safi, uint64_t flag,
int set)
@@ -14951,6 +15003,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
json_object_object_add(json_cap, "softwareVersion",
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 */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@@ -15378,6 +15440,21 @@ CPP_NOTICE("Remove `gracefulRestartCapability` JSON field")
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 */
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
@@ -18958,6 +19035,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
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 */
if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
@@ -19667,6 +19753,11 @@ int bgp_config_write(struct vty *vty)
? ""
: "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) !=
SAVE_BGP_DYNAMIC_CAPABILITY)
vty_out(vty,
@@ -20768,6 +20859,9 @@ void bgp_vty_init(void)
/* bgp default software-version-capability */
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 */
install_element(BGP_NODE, &bgp_default_dynamic_capability_cmd);
@@ -21423,6 +21517,9 @@ void bgp_vty_init(void)
/* "neighbor capability software-version" commands.*/
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.*/
install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
install_element(BGP_NODE,
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 6ee2a10185..a2b24b5f18 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1575,6 +1575,9 @@ struct peer *peer_new(struct bgp *bgp)
if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY))
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))
peer_flag_set(peer, PEER_FLAG_DYNAMIC_CAPABILITY);
@@ -2961,6 +2964,11 @@ static void peer_group2peer_config_copy(struct peer_group *group,
SET_FLAG(peer->flags,
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 */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
PEER_STR_ATTR_INHERIT(peer, group, password,
@@ -4813,6 +4821,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
{PEER_FLAG_EXTENDED_LINK_BANDWIDTH, 0, peer_change_none},
{PEER_FLAG_LONESOUL, 0, peer_change_reset_out},
{PEER_FLAG_TCP_MSS, 0, peer_change_none},
+ {PEER_FLAG_CAPABILITY_LINK_LOCAL, 0, peer_change_none},
{0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = {
@@ -4902,7 +4911,7 @@ static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
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_STRICT_CAP_MATCH || flag == PEER_FLAG_CAPABILITY_LINK_LOCAL)
peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
else if (flag == PEER_FLAG_PASSIVE)
peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index c72072852d..9ba6219beb 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -566,6 +566,7 @@ struct bgp {
#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40)
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_INSTALL (1ULL << 41)
#define BGP_FLAG_L3VNI_SCHEDULE_FOR_DELETE (1ULL << 42)
+#define BGP_FLAG_LINK_LOCAL_CAPABILITY (1ULL << 43)
/* BGP default address-families.
* 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_PATHS_LIMIT_ADV (1U << 29)
#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) */
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_EXTENDED_LINK_BANDWIDTH (1ULL << 39)
#define PEER_FLAG_DUAL_AS (1ULL << 40)
+#define PEER_FLAG_CAPABILITY_LINK_LOCAL (1ULL << 41)
/*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
@@ -2099,6 +2103,7 @@ struct bgp_nlri {
#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
#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). */
#define BGP_NOTIFY_CEASE_MAX_PREFIX 1