Skip to content

Commit 2ef76a3

Browse files
authored
Merge pull request FRRouting#17871 from opensourcerouting/feature/bgp_link_local_capability
bgpd: Implement Link-Local Next Hop capability
2 parents eebe460 + db853cc commit 2ef76a3

File tree

15 files changed

+356
-40
lines changed

15 files changed

+356
-40
lines changed

bgpd/bgp_attr.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,6 +2450,10 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
24502450
if (!peer->nexthop.ifp) {
24512451
zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
24522452
peer->host);
2453+
if (CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
2454+
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))
2455+
bgp_notify_send(peer->connection, BGP_NOTIFY_UPDATE_ERR,
2456+
BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP);
24532457
return BGP_ATTR_PARSE_WITHDRAW;
24542458
}
24552459
attr->nh_ifindex = peer->nexthop.ifp->ifindex;

bgpd/bgp_debug.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ static const struct message bgp_notify_update_msg[] = {
149149
{BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
150150
{BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
151151
{BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
152+
{BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP, "/Unreachable Link-Local Address"},
152153
{0}};
153154

154155
static const struct message bgp_notify_cease_msg[] = {

bgpd/bgp_nht.c

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extern struct zclient *zclient;
3838

3939
static void register_zebra_rnh(struct bgp_nexthop_cache *bnc);
4040
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
41-
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
41+
static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
4242
static void bgp_nht_ifp_initial(struct event *thread);
4343

4444
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,
330330

331331
/* This will return true if the global IPv6 NH is a link local
332332
* addr */
333-
if (make_prefix(afi, pi, &p) < 0)
333+
if (!make_prefix(afi, pi, &p))
334334
return 1;
335335

336336
/*
@@ -1026,7 +1026,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
10261026
* make_prefix - make a prefix structure from the path (essentially
10271027
* path's node.
10281028
*/
1029-
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
1029+
static bool make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
10301030
{
10311031

10321032
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)
10361036
struct bgp_dest *net = pi->net;
10371037
const struct prefix *p_orig = bgp_dest_get_prefix(net);
10381038
struct in_addr ipv4;
1039+
struct peer *peer = pi->peer;
1040+
struct attr *attr = pi->attr;
10391041

10401042
if (p_orig->family == AF_FLOWSPEC) {
1041-
if (!pi->peer)
1042-
return -1;
1043-
return bgp_flowspec_get_first_nh(pi->peer->bgp,
1044-
pi, p, afi);
1043+
if (!peer)
1044+
return false;
1045+
return bgp_flowspec_get_first_nh(peer->bgp, pi, p, afi);
10451046
}
10461047
memset(p, 0, sizeof(struct prefix));
10471048
switch (afi) {
@@ -1051,63 +1052,68 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
10511052
p->u.prefix4 = p_orig->u.prefix4;
10521053
p->prefixlen = p_orig->prefixlen;
10531054
} else {
1054-
if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
1055-
ipv4_mapped_ipv6_to_ipv4(
1056-
&pi->attr->mp_nexthop_global, &ipv4);
1055+
if (IS_MAPPED_IPV6(&attr->mp_nexthop_global)) {
1056+
ipv4_mapped_ipv6_to_ipv4(&attr->mp_nexthop_global, &ipv4);
10571057
p->u.prefix4 = ipv4;
10581058
p->prefixlen = IPV4_MAX_BITLEN;
10591059
} else {
10601060
if (p_orig->family == AF_EVPN)
1061-
p->u.prefix4 =
1062-
pi->attr->mp_nexthop_global_in;
1061+
p->u.prefix4 = attr->mp_nexthop_global_in;
10631062
else
1064-
p->u.prefix4 = pi->attr->nexthop;
1063+
p->u.prefix4 = attr->nexthop;
10651064
p->prefixlen = IPV4_MAX_BITLEN;
10661065
}
10671066
}
10681067
break;
10691068
case AFI_IP6:
10701069
p->family = AF_INET6;
1071-
if (pi->attr->srv6_l3vpn) {
1070+
if (attr->srv6_l3vpn) {
10721071
p->prefixlen = IPV6_MAX_BITLEN;
1073-
if (pi->attr->srv6_l3vpn->transposition_len != 0 &&
1072+
if (attr->srv6_l3vpn->transposition_len != 0 &&
10741073
BGP_PATH_INFO_NUM_LABELS(pi)) {
1075-
IPV6_ADDR_COPY(&p->u.prefix6, &pi->attr->srv6_l3vpn->sid);
1074+
IPV6_ADDR_COPY(&p->u.prefix6, &attr->srv6_l3vpn->sid);
10761075
transpose_sid(&p->u.prefix6,
10771076
decode_label(&pi->extra->labels->label[0]),
1078-
pi->attr->srv6_l3vpn->transposition_offset,
1079-
pi->attr->srv6_l3vpn->transposition_len);
1077+
attr->srv6_l3vpn->transposition_offset,
1078+
attr->srv6_l3vpn->transposition_len);
10801079
} else
1081-
IPV6_ADDR_COPY(&(p->u.prefix6), &(pi->attr->srv6_l3vpn->sid));
1080+
IPV6_ADDR_COPY(&(p->u.prefix6), &(attr->srv6_l3vpn->sid));
10821081
} else if (is_bgp_static) {
10831082
p->u.prefix6 = p_orig->u.prefix6;
10841083
p->prefixlen = p_orig->prefixlen;
10851084
} else {
10861085
/* If we receive MP_REACH nexthop with ::(LL)
10871086
* or LL(LL), use LL address as nexthop cache.
10881087
*/
1089-
if (pi->attr &&
1090-
pi->attr->mp_nexthop_len ==
1091-
BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
1092-
(IN6_IS_ADDR_UNSPECIFIED(
1093-
&pi->attr->mp_nexthop_global) ||
1094-
IN6_IS_ADDR_LINKLOCAL(&pi->attr->mp_nexthop_global)))
1095-
p->u.prefix6 = pi->attr->mp_nexthop_local;
1088+
if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
1089+
(IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
1090+
IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)))
1091+
p->u.prefix6 = attr->mp_nexthop_local;
10961092
/* If we receive MR_REACH with (GA)::(LL)
10971093
* then check for route-map to choose GA or LL
10981094
*/
1099-
else if (pi->attr &&
1100-
pi->attr->mp_nexthop_len ==
1101-
BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
1102-
if (CHECK_FLAG(pi->attr->nh_flags,
1103-
BGP_ATTR_NH_MP_PREFER_GLOBAL))
1104-
p->u.prefix6 =
1105-
pi->attr->mp_nexthop_global;
1095+
else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
1096+
if (CHECK_FLAG(attr->nh_flags, BGP_ATTR_NH_MP_PREFER_GLOBAL))
1097+
p->u.prefix6 = attr->mp_nexthop_global;
11061098
else
1107-
p->u.prefix6 =
1108-
pi->attr->mp_nexthop_local;
1099+
p->u.prefix6 = attr->mp_nexthop_local;
1100+
} else if (attr && attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL &&
1101+
IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
1102+
/* If we receive MP_REACH with GUA as LL, we should
1103+
* check if we have Link-Local Next Hop capability also.
1104+
*/
1105+
if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
1106+
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV))) {
1107+
zlog_warn("%s: received IPv6 global next-hop as Link-Local, but no capability exchanged",
1108+
__func__);
1109+
p->u.prefix6 = attr->mp_nexthop_global;
1110+
} else {
1111+
p->u.prefix6 = attr->mp_nexthop_global;
1112+
p->prefixlen = IPV6_MAX_BITLEN;
1113+
return false;
1114+
}
11091115
} else
1110-
p->u.prefix6 = pi->attr->mp_nexthop_global;
1116+
p->u.prefix6 = attr->mp_nexthop_global;
11111117
p->prefixlen = IPV6_MAX_BITLEN;
11121118
}
11131119
break;
@@ -1119,7 +1125,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
11191125
}
11201126
break;
11211127
}
1122-
return 0;
1128+
return true;
11231129
}
11241130

11251131
/**

bgpd/bgp_open.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const struct message capcode_str[] = {
4343
{ CAPABILITY_CODE_ROLE, "Role" },
4444
{ CAPABILITY_CODE_SOFT_VERSION, "Software Version" },
4545
{ CAPABILITY_CODE_PATHS_LIMIT, "Paths-Limit" },
46+
{ CAPABILITY_CODE_LINK_LOCAL, "Link-Local Next Hop" },
4647
{ 0 }
4748
};
4849

@@ -63,6 +64,7 @@ const size_t cap_minsizes[] = {
6364
[CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
6465
[CAPABILITY_CODE_SOFT_VERSION] = CAPABILITY_CODE_SOFT_VERSION_LEN,
6566
[CAPABILITY_CODE_PATHS_LIMIT] = CAPABILITY_CODE_PATHS_LIMIT_LEN,
67+
[CAPABILITY_CODE_LINK_LOCAL] = CAPABILITY_CODE_LINK_LOCAL_LEN,
6668
};
6769

6870
/* value the capability must be a multiple of.
@@ -1067,6 +1069,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
10671069
case CAPABILITY_CODE_ROLE:
10681070
case CAPABILITY_CODE_SOFT_VERSION:
10691071
case CAPABILITY_CODE_PATHS_LIMIT:
1072+
case CAPABILITY_CODE_LINK_LOCAL:
10701073
/* Check length. */
10711074
if (caphdr.length < cap_minsizes[caphdr.code]) {
10721075
zlog_info(
@@ -1168,6 +1171,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
11681171
case CAPABILITY_CODE_SOFT_VERSION:
11691172
ret = bgp_capability_software_version(peer, &caphdr);
11701173
break;
1174+
case CAPABILITY_CODE_LINK_LOCAL:
1175+
SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV);
1176+
break;
11711177
case CAPABILITY_CODE_PATHS_LIMIT:
11721178
ret = bgp_capability_paths_limit(peer, &caphdr);
11731179
break;
@@ -1968,6 +1974,16 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
19681974
stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
19691975
}
19701976

1977+
/* Link-Local Next Hop capability. */
1978+
if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_LINK_LOCAL)) {
1979+
SET_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV);
1980+
stream_putc(s, BGP_OPEN_OPT_CAP);
1981+
ext_opt_params ? stream_putw(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2)
1982+
: stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN + 2);
1983+
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
1984+
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL_LEN);
1985+
}
1986+
19711987
/* FQDN capability */
19721988
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN)
19731989
&& cmd_hostname_get()) {

bgpd/bgp_open.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct graceful_restart_af {
5454
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
5555
#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
5656
#define CAPABILITY_CODE_PATHS_LIMIT 76 /* Paths Limit Capability */
57+
#define CAPABILITY_CODE_LINK_LOCAL 77 /* draft-white-linklocal-capability */
5758

5859
/* Capability Length */
5960
#define CAPABILITY_CODE_MP_LEN 4
@@ -71,6 +72,7 @@ struct graceful_restart_af {
7172
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
7273
#define CAPABILITY_CODE_ROLE_LEN 1
7374
#define CAPABILITY_CODE_SOFT_VERSION_LEN 1
75+
#define CAPABILITY_CODE_LINK_LOCAL_LEN 0
7476

7577
/* Cooperative Route Filtering Capability. */
7678

bgpd/bgp_packet.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,18 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
12451245

12461246
/* Encode MP_EXT capability. */
12471247
switch (capability_code) {
1248+
case CAPABILITY_CODE_LINK_LOCAL:
1249+
stream_putc(s, action);
1250+
stream_putc(s, CAPABILITY_CODE_LINK_LOCAL);
1251+
stream_putc(s, 0);
1252+
1253+
if (bgp_debug_neighbor_events(peer))
1254+
zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", peer,
1255+
action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing",
1256+
capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
1257+
1258+
COND_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV, action == CAPABILITY_ACTION_SET);
1259+
break;
12481260
case CAPABILITY_CODE_SOFT_VERSION:
12491261
stream_putc(s, action);
12501262
stream_putc(s, CAPABILITY_CODE_SOFT_VERSION);
@@ -3769,6 +3781,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
37693781
case CAPABILITY_CODE_ROLE:
37703782
case CAPABILITY_CODE_SOFT_VERSION:
37713783
case CAPABILITY_CODE_PATHS_LIMIT:
3784+
case CAPABILITY_CODE_LINK_LOCAL:
37723785
if (hdr->length < cap_minsizes[hdr->code]) {
37733786
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
37743787
peer, capability, hdr->length,

bgpd/bgp_route.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,8 +2506,16 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
25062506
} else if (!ibgp_to_ibgp && !transparent &&
25072507
!CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) &&
25082508
IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network &&
2509-
(from == bgp->peer_self || peer->sort == BGP_PEER_EBGP))
2510-
global_and_ll = true;
2509+
(from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)) {
2510+
/* If an implementation intends to send a single link-local forwarding
2511+
* address in the Next Hop field of the MP_REACH_NLRI, it MUST set the
2512+
* length of the Next Hop field to 16 and include only the IPv6 link-local
2513+
* address in the Next Hop field.
2514+
*/
2515+
if (!(CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_ADV) &&
2516+
CHECK_FLAG(peer->cap, PEER_CAP_LINK_LOCAL_RCV)))
2517+
global_and_ll = true;
2518+
}
25112519

25122520
if (global_and_ll) {
25132521
if (safi == SAFI_MPLS_VPN)
@@ -9946,6 +9954,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
99469954
"ipv6");
99479955
json_object_string_add(json_nexthop_global, "scope",
99489956
"global");
9957+
json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
99499958

99509959
/* We display both LL & GL if both have been
99519960
* received */
@@ -9969,6 +9978,8 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
99699978
"ipv6");
99709979
json_object_string_add(json_nexthop_ll, "scope",
99719980
"link-local");
9981+
json_object_int_add(json_nexthop_global, "length",
9982+
attr->mp_nexthop_len);
99729983

99739984
if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
99749985
&attr->mp_nexthop_local) !=
@@ -11090,6 +11101,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
1109011101
"ipv6");
1109111102
json_object_string_add(json_nexthop_global, "scope",
1109211103
"global");
11104+
json_object_int_add(json_nexthop_global, "length", attr->mp_nexthop_len);
1109311105
} else {
1109411106
if (nexthop_hostname)
1109511107
vty_out(vty, " %pI6(%s)",
@@ -11277,6 +11289,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
1127711289
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
1127811290
json_object_string_add(json_nexthop_ll, "scope",
1127911291
"link-local");
11292+
json_object_int_add(json_nexthop_ll, "length", attr->mp_nexthop_len);
1128011293

1128111294
json_object_boolean_true_add(json_nexthop_ll,
1128211295
"accessible");

0 commit comments

Comments
 (0)