From d69fa2f85e705a9d4b730782c46cf63c4985e38b Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Wed, 8 Jan 2025 22:51:14 +0200 Subject: [PATCH] bgpd: Implement Link-Local Next Hop capability Related: https://datatracker.ietf.org/doc/html/draft-white-linklocal-capability TL;DR; use 16 bytes long next-hops for point-to-point (unnumbered) links instead of sending 32 bytes (::/LL, GUA/LL, LL/LL combinations). For backward compatiblity we should handle even 32 bytes existing next hops. Signed-off-by: Donatas Abraitis --- bgpd/bgp_attr.c | 4 ++ bgpd/bgp_debug.c | 1 + bgpd/bgp_nht.c | 79 ++++++++++++++++++++------------------ bgpd/bgp_open.c | 16 ++++++++ bgpd/bgp_open.h | 2 + bgpd/bgp_packet.c | 13 +++++++ bgpd/bgp_route.c | 12 +++++- bgpd/bgp_vty.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgpd.c | 12 ++++-- bgpd/bgpd.h | 5 +++ 10 files changed, 199 insertions(+), 42 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index d349922c5227..c15dada9c1a6 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 319638e41244..dfebc00e0a09 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 164e2300c09e..5deb08a39045 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,34 @@ 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__); + } 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 +1124,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 6451c7cf3897..be04d87b7443 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 3a8cba9b7d33..abe3b51f5df4 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 ca2e8de04173..ca0d414dbbec 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 19d246bdcbff..09420f2f6b5f 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 33b220d3ec70..0c5d0b10b045 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 $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 6ee2a10185f9..55f5c1499e60 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1572,8 +1572,8 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->flags, PEER_FLAG_ENFORCE_FIRST_AS); } - 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 +2961,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 +4818,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 +4908,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 c72072852d1b..9ba6219bebbc 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