Skip to content

Commit

Permalink
bgpd: Implement Link-Local Next Hop capability
Browse files Browse the repository at this point in the history
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 <donatas@opensourcerouting.org>
  • Loading branch information
ton31337 committed Jan 17, 2025
1 parent d3c46bc commit d69fa2f
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 42 deletions.
4 changes: 4 additions & 0 deletions bgpd/bgp_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions bgpd/bgp_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
79 changes: 42 additions & 37 deletions bgpd/bgp_nht.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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;

/*
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -1051,63 +1052,67 @@ 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;
} else {
/* 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;
Expand All @@ -1119,7 +1124,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
}
break;
}
return 0;
return true;
}

/**
Expand Down
16 changes: 16 additions & 0 deletions bgpd/bgp_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};

Expand All @@ -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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down
2 changes: 2 additions & 0 deletions bgpd/bgp_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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. */

Expand Down
13 changes: 13 additions & 0 deletions bgpd/bgp_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 10 additions & 2 deletions bgpd/bgp_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit d69fa2f

Please sign in to comment.