bgpd: Implement BGP Next Hop Dependent Characteristics Attribute

https://datatracker.ietf.org/doc/html/draft-ietf-idr-entropy-label-16
https://datatracker.ietf.org/doc/html/draft-wang-idr-next-next-hop-nodes-02

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
Donatas Abraitis 2025-04-29 03:22:50 +03:00
parent bd1a1ff5db
commit 731b0fc70d
11 changed files with 544 additions and 31 deletions

View file

@ -32,7 +32,9 @@
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_mpath.h"
#include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_nhc.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
#include "bgp_encap_types.h"
@ -69,6 +71,7 @@ static const struct message attr_str[] = {
{BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
{BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
{BGP_ATTR_AIGP, "AIGP"},
{BGP_ATTR_NHC, "Next Hop Dependent Characteristics"},
{0}};
static const struct message attr_flag_str[] = {
@ -199,6 +202,7 @@ static struct hash *vnc_hash = NULL;
static struct hash *srv6_l3vpn_hash;
static struct hash *srv6_vpn_hash;
static struct hash *evpn_overlay_hash;
static struct hash *bgp_nhc_hash;
struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
{
@ -614,6 +618,91 @@ static void evpn_overlay_finish(void)
(void (*)(void *))evpn_overlay_free);
}
static void *bgp_nhc_hash_alloc(void *p)
{
return p;
}
static struct bgp_nhc *bgp_nhc_intern(struct bgp_nhc *nhc)
{
struct bgp_nhc *find;
find = hash_get(bgp_nhc_hash, nhc, bgp_nhc_hash_alloc);
if (find != nhc)
bgp_nhc_free(nhc);
find->refcnt++;
return find;
}
static void bgp_nhc_unintern(struct bgp_nhc **nhcp)
{
struct bgp_nhc *nhc = *nhcp;
if (!*nhcp)
return;
if (nhc->refcnt)
nhc->refcnt--;
if (nhc->refcnt == 0) {
hash_release(bgp_nhc_hash, nhc);
bgp_nhc_free(nhc);
*nhcp = NULL;
}
}
static uint32_t bgp_nhc_hash_key_make(const void *p)
{
const struct bgp_nhc *nhc = p;
uint32_t key = 0;
key = jhash(&nhc->afi, sizeof(nhc->afi), key);
key = jhash(&nhc->safi, sizeof(nhc->safi), key);
key = jhash(&nhc->nh_length, sizeof(nhc->nh_length), key);
key = jhash(&nhc->nh_ipv4, IPV4_MAX_BYTELEN, key);
key = jhash(&nhc->nh_ipv6, IPV6_MAX_BYTELEN, key);
key = jhash(&nhc->tlvs_length, sizeof(nhc->tlvs_length), key);
if (nhc->tlvs)
key = jhash(nhc->tlvs, nhc->tlvs_length, key);
return key;
}
static bool bgp_nhc_hash_cmp(const void *p1, const void *p2)
{
const struct bgp_nhc *nhc1 = p1;
const struct bgp_nhc *nhc2 = p2;
if (nhc1 == nhc2)
return true;
if (!nhc1 || !nhc2)
return false;
if (!nhc1->tlvs || !nhc2->tlvs)
return false;
return (nhc1->afi == nhc2->afi && nhc1->safi == nhc2->safi &&
nhc1->nh_length == nhc2->nh_length &&
IPV4_ADDR_CMP(&nhc1->nh_ipv4, &nhc2->nh_ipv4) &&
IPV6_ADDR_CMP(&nhc1->nh_ipv6, &nhc2->nh_ipv6) &&
nhc1->tlvs_length == nhc2->tlvs_length &&
memcmp(nhc1->tlvs, nhc2->tlvs, nhc1->tlvs_length) == 0);
}
static void nhc_init(void)
{
bgp_nhc_hash = hash_create(bgp_nhc_hash_key_make, bgp_nhc_hash_cmp,
"BGP Next Hop Dependent Characteristics");
}
static void nhc_finish(void)
{
hash_clean_and_free(&bgp_nhc_hash, (void (*)(void *))bgp_nhc_free);
}
static void *srv6_l3vpn_hash_alloc(void *p)
{
return p;
@ -866,6 +955,8 @@ unsigned int attrhash_key_make(const void *p)
MIX3(attr->mm_seqnum, attr->df_alg, attr->df_pref);
MIX(attr->encap_tunneltype);
key = jhash(&attr->rmac, sizeof(attr->rmac), key);
if (bgp_attr_get_nhc(attr))
MIX(bgp_nhc_hash_key_make(bgp_attr_get_nhc(attr)));
return key;
}
@ -882,47 +973,36 @@ bool attrhash_cmp(const void *p1, const void *p2)
attr1->med == attr2->med && attr1->local_pref == attr2->local_pref &&
attr1->rmap_change_flags == attr2->rmap_change_flags) {
if (attr1->aggregator_as == attr2->aggregator_as &&
attr1->aggregator_addr.s_addr ==
attr2->aggregator_addr.s_addr &&
attr1->aggregator_addr.s_addr == attr2->aggregator_addr.s_addr &&
attr1->weight == attr2->weight && attr1->tag == attr2->tag &&
attr1->label_index == attr2->label_index &&
attr1->mp_nexthop_len == attr2->mp_nexthop_len &&
bgp_attr_get_ecommunity(attr1) ==
bgp_attr_get_ecommunity(attr2) &&
bgp_attr_get_ipv6_ecommunity(attr1) ==
bgp_attr_get_ipv6_ecommunity(attr2) &&
bgp_attr_get_lcommunity(attr1) ==
bgp_attr_get_lcommunity(attr2) &&
bgp_attr_get_ecommunity(attr1) == bgp_attr_get_ecommunity(attr2) &&
bgp_attr_get_ipv6_ecommunity(attr1) == bgp_attr_get_ipv6_ecommunity(attr2) &&
bgp_attr_get_lcommunity(attr1) == bgp_attr_get_lcommunity(attr2) &&
bgp_attr_get_cluster(attr1) == bgp_attr_get_cluster(attr2) &&
bgp_attr_get_transit(attr1) == bgp_attr_get_transit(attr2) &&
bgp_attr_get_aigp_metric(attr1) ==
bgp_attr_get_aigp_metric(attr2) &&
bgp_attr_get_nhc(attr1) == bgp_attr_get_nhc(attr2) &&
bgp_attr_get_aigp_metric(attr1) == bgp_attr_get_aigp_metric(attr2) &&
attr1->rmap_table_id == attr2->rmap_table_id &&
(attr1->encap_tunneltype == attr2->encap_tunneltype) &&
encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs)
#ifdef ENABLE_BGP_VNC
&& encap_same(bgp_attr_get_vnc_subtlvs(attr1),
bgp_attr_get_vnc_subtlvs(attr2))
&& encap_same(bgp_attr_get_vnc_subtlvs(attr1), bgp_attr_get_vnc_subtlvs(attr2))
#endif
&& IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
&attr2->mp_nexthop_global) &&
IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
&attr2->mp_nexthop_local) &&
IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
&attr2->mp_nexthop_global_in) &&
IPV4_ADDR_SAME(&attr1->originator_id,
&attr2->originator_id) &&
&& IPV6_ADDR_SAME(&attr1->mp_nexthop_global, &attr2->mp_nexthop_global) &&
IPV6_ADDR_SAME(&attr1->mp_nexthop_local, &attr2->mp_nexthop_local) &&
IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, &attr2->mp_nexthop_global_in) &&
IPV4_ADDR_SAME(&attr1->originator_id, &attr2->originator_id) &&
overlay_index_same(attr1, attr2) &&
!memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) &&
attr1->es_flags == attr2->es_flags &&
attr1->mm_seqnum == attr2->mm_seqnum &&
attr1->mm_sync_seqnum == attr2->mm_sync_seqnum &&
attr1->df_pref == attr2->df_pref &&
attr1->df_alg == attr2->df_alg &&
attr1->df_pref == attr2->df_pref && attr1->df_alg == attr2->df_alg &&
attr1->nh_ifindex == attr2->nh_ifindex &&
attr1->nh_lla_ifindex == attr2->nh_lla_ifindex &&
attr1->nh_flags == attr2->nh_flags &&
attr1->distance == attr2->distance &&
attr1->nh_flags == attr2->nh_flags && attr1->distance == attr2->distance &&
srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) &&
srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) &&
attr1->srte_color == attr2->srte_color &&
@ -1125,6 +1205,15 @@ struct attr *bgp_attr_intern(struct attr *attr)
}
#endif
struct bgp_nhc *nhc = bgp_attr_get_nhc(attr);
if (nhc) {
if (!nhc->refcnt)
bgp_attr_set_nhc(attr, bgp_nhc_intern(nhc));
else
nhc->refcnt++;
}
/* At this point, attr only contains intern'd pointers. that means
* if we find it in attrhash, it has all the same pointers and we
* correctly updated the refcounts on these.
@ -1288,6 +1377,7 @@ void bgp_attr_unintern_sub(struct attr *attr)
struct community *comm = NULL;
struct transit *transit;
struct bgp_route_evpn *bre;
struct bgp_nhc *nhc;
/* aspath refcount shoud be decrement. */
aspath_unintern(&attr->aspath);
@ -1317,6 +1407,10 @@ void bgp_attr_unintern_sub(struct attr *attr)
transit_unintern(&transit);
bgp_attr_set_transit(attr, NULL);
nhc = bgp_attr_get_nhc(attr);
bgp_nhc_unintern(&nhc);
bgp_attr_set_nhc(attr, NULL);
encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
#ifdef ENABLE_BGP_VNC
@ -1366,6 +1460,7 @@ void bgp_attr_flush(struct attr *attr)
struct lcommunity *lcomm;
struct community *comm;
struct bgp_route_evpn *bre;
struct bgp_nhc *nhc;
if (attr->aspath && !attr->aspath->refcnt) {
aspath_free(attr->aspath);
@ -1429,6 +1524,12 @@ void bgp_attr_flush(struct attr *attr)
evpn_overlay_free(bre);
bgp_attr_set_evpn_overlay(attr, NULL);
}
nhc = bgp_attr_get_nhc(attr);
if (nhc && !nhc->refcnt) {
bgp_nhc_free(nhc);
bgp_attr_set_nhc(attr, NULL);
}
}
/* Implement draft-scudder-idr-optional-transitive behaviour and
@ -1491,6 +1592,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
case BGP_ATTR_AGGREGATOR:
case BGP_ATTR_ATOMIC_AGGREGATE:
case BGP_ATTR_PREFIX_SID:
case BGP_ATTR_NHC:
return BGP_ATTR_PARSE_PROCEED;
/* Core attributes, particularly ones which may influence route
@ -1600,6 +1702,7 @@ const uint8_t attr_flags_values[] = {
[BGP_ATTR_IPV6_EXT_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL,
[BGP_ATTR_NHC] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
};
static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
@ -3453,6 +3556,155 @@ aigp_ignore:
return bgp_attr_ignore(peer, args->type);
}
static int bgp_attr_nhc(struct bgp_attr_parser_args *args)
{
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
bgp_size_t length = args->length;
uint8_t type = args->type;
iana_afi_t pkt_afi;
afi_t afi;
iana_safi_t pkt_safi;
safi_t safi;
struct stream *s = BGP_INPUT(peer);
struct bgp_nhc *nhc = bgp_attr_get_nhc(attr);
uint16_t tlv_code;
uint16_t tlv_length;
size_t tlv_headersz = sizeof(tlv_code) + sizeof(tlv_length);
struct bgp_nhc_tlv *tlv;
if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
goto nhc_ignore;
if (length < BGP_NHC_MIN_LEN) {
zlog_err("%pBP sent BGP NHC attribute length is too short: %d", peer, length);
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total);
}
pkt_afi = stream_getw(s);
pkt_safi = stream_getc(s);
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
if (bgp_debug_update(peer, NULL, NULL, 0))
zlog_debug("%pBP sent unrecognizable AFI, %s or, SAFI, %s, of NHC", peer,
iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
return BGP_ATTR_PARSE_ERROR;
}
if (bgp_debug_update(peer, NULL, NULL, 0))
zlog_debug("%pBP sent BGP NHC attribute with length %d for afi %s, safi %s", peer,
length, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
if (!nhc)
nhc = XCALLOC(MTYPE_BGP_NHC, sizeof(struct bgp_nhc));
nhc->afi = afi;
nhc->safi = safi;
nhc->nh_length = stream_getc(s);
/* If Next-hop is IPv6, we should check if we are not out of bound too */
if (nhc->nh_length == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
if (length < BGP_NHC_MIN_IPV6_LEN) {
zlog_err("%pBP sent BGP NHC attribute length is too short: %d", peer,
length);
bgp_nhc_free(nhc);
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total);
}
}
/* Next-hop length should be either 4 or 16 */
if (nhc->nh_length != BGP_ATTR_NHLEN_IPV4 && nhc->nh_length != BGP_ATTR_NHLEN_IPV6_GLOBAL) {
zlog_err("%pBP sent wrong next-hop length, %d, in NHC", peer, nhc->nh_length);
bgp_nhc_free(nhc);
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total);
}
length -= 4; /* AFI(2) + SAFI(1) + Next-hop length(1) */
if (nhc->nh_length == BGP_ATTR_NHLEN_IPV4) {
stream_get(&nhc->nh_ipv4, s, IPV4_MAX_BYTELEN);
length -= IPV4_MAX_BYTELEN;
} else if (nhc->nh_length == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
stream_get(&nhc->nh_ipv6, s, IPV6_MAX_BYTELEN);
length -= IPV6_MAX_BYTELEN;
if (IN6_IS_ADDR_LINKLOCAL(&nhc->nh_ipv6)) {
if (!peer->nexthop.ifp) {
zlog_warn("%pBP sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
peer);
if (PEER_HAS_LINK_LOCAL_CAPABILITY(peer))
bgp_notify_send(peer->connection, BGP_NOTIFY_UPDATE_ERR,
BGP_NOTIFY_UPDATE_UNREACH_NEXT_HOP);
return BGP_ATTR_PARSE_PROCEED;
}
}
} else {
zlog_err("%pBP sent wrong next-hop length, %d, in NHC", peer, attr->mp_nexthop_len);
bgp_nhc_free(nhc);
return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
}
/*
* draft-ietf-idr-entropy-label-17
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Characteristic Code | Characteristic Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ Characteristic Value (variable) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
while (length && STREAM_READABLE(s) >= BGP_NHC_TLV_MIN_LEN) {
struct bgp_nhc_tlv *found;
tlv_code = stream_getw(s);
tlv_length = stream_getw(s);
if (length < tlv_length || STREAM_READABLE(s) < tlv_length) {
zlog_err("%pBP sent BGP NHC TLV length %d exceeds remaining stream length %zu",
peer, tlv_length, STREAM_READABLE(s));
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total);
}
tlv = XCALLOC(MTYPE_BGP_NHC_TLV, sizeof(struct bgp_nhc_tlv) + tlv_length);
tlv->code = tlv_code;
tlv->length = tlv_length;
tlv->value = XCALLOC(MTYPE_BGP_NHC_TLV_VAL, tlv_length);
stream_get(tlv->value, s, tlv_length);
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug("%pBP sent BGP NHC TLV code %d, length %d, value %p", peer,
tlv->code, tlv->length, tlv->value);
/* draft-wang-idr-next-next-hop-nodes */
if (tlv->code == BGP_ATTR_NHC_TLV_NNHN) {
uint16_t len = tlv->length;
if (len % IPV4_MAX_BYTELEN != 0) {
zlog_err("%pBP sent BGP NHC (NNHN TLV) length %d not a multiple of %d",
peer, tlv->length, IPV4_MAX_BYTELEN);
bgp_nhc_tlv_free(tlv);
return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
args->total);
}
}
found = bgp_nhc_tlv_find(nhc, tlv_code);
if (found)
bgp_nhc_tlv_free(found);
bgp_nhc_tlv_add(nhc, tlv);
length -= tlv_length + tlv_headersz;
}
bgp_attr_set_nhc(attr, bgp_nhc_intern(nhc));
return BGP_ATTR_PARSE_PROCEED;
nhc_ignore:
stream_forward_getp(s, length);
return bgp_attr_ignore(peer, type);
}
/* OTC attribute. */
static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
{
@ -3915,6 +4167,9 @@ enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr,
case BGP_ATTR_AIGP:
ret = bgp_attr_aigp(&attr_args);
break;
case BGP_ATTR_NHC:
ret = bgp_attr_nhc(&attr_args);
break;
default:
ret = bgp_attr_unknown(&attr_args);
break;
@ -4276,6 +4531,70 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
return sizep;
}
static void bgp_packet_nhc(struct stream *s, struct peer *peer, afi_t afi, safi_t safi,
struct attr *attr, struct bgp_path_info *bpi)
{
size_t sizep;
iana_afi_t pkt_afi = IANA_AFI_IPV4;
iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
afi_t nh_afi;
struct bgp_path_info *exists;
uint16_t total;
if (!bpi)
return;
total = bgp_path_info_mpath_count(bpi) * IPV4_MAX_BYTELEN;
/* NHC now supports only draft-wang-idr-next-next-hop-nodes, thus
* do not sent NHC attribute if the path is not multipath or self
* originated.
*/
if (bpi->peer == bpi->peer->bgp->peer_self || bgp_path_info_mpath_count(bpi) < 2)
return;
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_NHC);
sizep = stream_get_endp(s);
stream_putc(s, 0);
/* Convert AFI, SAFI to values for packet. */
bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
stream_putw(s, pkt_afi);
stream_putc(s, pkt_safi);
if (afi == AFI_IP && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST ||
safi == SAFI_MPLS_VPN || safi == SAFI_MULTICAST))
nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP;
else if (safi == SAFI_FLOWSPEC)
nh_afi = afi;
else
nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len);
if (nh_afi == AFI_IP) {
stream_putc(s, IPV4_MAX_BYTELEN);
stream_put_ipv4(s, attr->nexthop.s_addr);
} else {
stream_putc(s, IPV6_MAX_BYTELEN);
stream_put(s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
}
/* Put TLVs */
/* Begin NNHN TLV */
stream_putw(s, BGP_ATTR_NHC_TLV_NNHN);
stream_putw(s, total);
stream_put_ipv4(s, bpi->peer->remote_id.s_addr);
for (exists = bgp_path_info_mpath_first(bpi); exists;
exists = bgp_path_info_mpath_next(exists))
stream_put_ipv4(s, exists->peer->remote_id.s_addr);
/* End NNHN TLV */
stream_putc_at(s, sizep, (stream_get_endp(s) - sizep) - 1);
}
void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
const struct prefix *p,
const struct prefix_rd *prd, mpls_label_t *label,
@ -4510,15 +4829,16 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct strea
struct attr *attr, struct bpacket_attr_vec_arr *vecarr,
struct prefix *p, afi_t afi, safi_t safi, struct peer *from,
struct prefix_rd *prd, mpls_label_t *label, uint8_t num_labels,
bool addpath_capable, uint32_t addpath_tx_id)
bool addpath_capable, uint32_t addpath_tx_id,
struct bgp_path_info *bpi)
{
size_t cp;
size_t aspath_sizep;
struct aspath *aspath;
int send_as4_path = 0;
int send_as4_aggregator = 0;
bool use32bit = CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
&& CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV);
bool use32bit = CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) &&
CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV);
if (!bgp)
bgp = peer->bgp;
@ -4993,6 +5313,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct strea
stream_put_bgp_aigp_tlv_metric(s, attr->aigp_metric);
}
/* draft-ietf-idr-entropy-label */
bgp_packet_nhc(s, peer, afi, safi, attr, bpi);
/* Unknown transit attribute. */
struct transit *transit = bgp_attr_get_transit(attr);
@ -5067,6 +5390,7 @@ void bgp_attr_init(void)
encap_init();
srv6_init();
evpn_overlay_init();
nhc_init();
}
void bgp_attr_finish(void)
@ -5081,6 +5405,7 @@ void bgp_attr_finish(void)
encap_finish();
srv6_finish();
evpn_overlay_finish();
nhc_finish();
}
/* Make attribute packet. */

View file

@ -318,6 +318,9 @@ struct attr {
/* AIGP Metric */
uint64_t aigp_metric;
/* Next-hop characteristics */
struct bgp_nhc *nhc;
};
/* rmap_change_flags definition */
@ -391,7 +394,7 @@ extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struc
struct prefix *p, afi_t afi, safi_t safi, struct peer *from,
struct prefix_rd *prd, mpls_label_t *label,
uint8_t num_labels, bool addpath_capable,
uint32_t addpath_tx_id);
uint32_t addpath_tx_id, struct bgp_path_info *bpi);
extern void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi,
const struct prefix *p);
extern bool attrhash_cmp(const void *arg1, const void *arg2);
@ -584,6 +587,21 @@ static inline void bgp_attr_set_transit(struct attr *attr,
attr->transit = transit;
}
static inline struct bgp_nhc *bgp_attr_get_nhc(const struct attr *attr)
{
return attr->nhc;
}
static inline void bgp_attr_set_nhc(struct attr *attr, struct bgp_nhc *bnc)
{
attr->nhc = bnc;
if (bnc)
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NHC));
else
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NHC));
}
#define AIGP_TRANSMIT_ALLOWED(peer) \
(CHECK_FLAG((peer)->flags, PEER_FLAG_AIGP) || ((peer)->sub_sort == BGP_PEER_EBGP_OAD) || \
((peer)->sort != BGP_PEER_EBGP))

View file

@ -1137,7 +1137,7 @@ static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd,
/* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, &vecarr, NULL, afi, safi, peer,
NULL, NULL, 0, 0, 0);
NULL, NULL, 0, 0, 0, NULL);
/* space check? */

View file

@ -137,3 +137,7 @@ DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
DEFINE_MTYPE(BGPD, BGP_SOFT_VERSION, "Software Version");
DEFINE_MTYPE(BGPD, BGP_EVPN_OVERLAY, "BGP EVPN Overlay");
DEFINE_MTYPE(BGPD, BGP_NHC, "BGP NHC");
DEFINE_MTYPE(BGPD, BGP_NHC_TLV, "BGP NHC TLV");
DEFINE_MTYPE(BGPD, BGP_NHC_TLV_VAL, "BGP NHC TLV value");

View file

@ -136,6 +136,10 @@ DECLARE_MTYPE(BGP_SOFT_VERSION);
DECLARE_MTYPE(BGP_EVPN_OVERLAY);
DECLARE_MTYPE(BGP_NHC);
DECLARE_MTYPE(BGP_NHC_TLV);
DECLARE_MTYPE(BGP_NHC_TLV_VAL);
DECLARE_MTYPE(CLEARING_BATCH);
#endif /* _QUAGGA_BGP_MEMORY_H */

67
bgpd/bgp_nhc.c Normal file
View file

@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2025, Donatas Abraitis <donatas@opensourcerouting.org>
*/
#include "bgp_nhc.h"
void bgp_nhc_tlv_add(struct bgp_nhc *nhc, struct bgp_nhc_tlv *tlv)
{
struct bgp_nhc_tlv *last;
if (!tlv)
return;
for (last = nhc->tlvs; last && last->next; last = last->next)
;
if (last)
last->next = tlv;
else
nhc->tlvs = tlv;
nhc->tlvs_length += tlv->length + BGP_NHC_TLV_MIN_LEN;
}
struct bgp_nhc_tlv *bgp_nhc_tlv_find(struct bgp_nhc *nhc, uint16_t code)
{
struct bgp_nhc_tlv *tlv = NULL;
if (!nhc)
return tlv;
for (tlv = nhc->tlvs; tlv; tlv = tlv->next) {
if (tlv->code == code)
return tlv;
}
return tlv;
}
void bgp_nhc_tlv_free(struct bgp_nhc_tlv *tlv)
{
if (!tlv)
return;
if (tlv->value)
XFREE(MTYPE_BGP_NHC_TLV_VAL, tlv->value);
XFREE(MTYPE_BGP_NHC_TLV, tlv);
}
void bgp_nhc_tlvs_free(struct bgp_nhc_tlv *tlv)
{
struct bgp_nhc_tlv *next;
while (tlv) {
next = tlv->next;
bgp_nhc_tlv_free(tlv);
tlv = next;
}
}
void bgp_nhc_free(struct bgp_nhc *nhc)
{
bgp_nhc_tlvs_free(nhc->tlvs);
XFREE(MTYPE_BGP_NHC, nhc);
}

57
bgpd/bgp_nhc.h Normal file
View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2025, Donatas Abraitis <donatas@opensourcerouting.org>
*/
#ifndef _FRR_BGP_NHC_H
#define _FRR_BGP_NHC_H
#include "zebra.h"
#include <bgpd/bgpd.h>
#include <bgpd/bgp_attr.h>
struct bgp_nhc_tlv {
struct bgp_nhc_tlv *next;
uint16_t code;
uint16_t length;
uint8_t *value;
};
struct bgp_nhc {
unsigned long refcnt;
uint16_t afi;
uint8_t safi;
uint8_t nh_length;
struct in_addr nh_ipv4;
struct in6_addr nh_ipv6;
uint16_t tlvs_length;
struct bgp_nhc_tlv *tlvs;
};
/* 4 => Characteristic Code + Characteristic Length */
#define BGP_NHC_TLV_MIN_LEN 4
/*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Family Identifier | SAFI | Next Hop Len |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ Network Address of Next Hop (variable) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Characteristic Code | Characteristic Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ Characteristic Value (variable) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#define BGP_NHC_MIN_LEN 12
#define BGP_NHC_MIN_IPV6_LEN 24
/* TLV values: */
/* draft-wang-idr-next-next-hop-nodes */
#define BGP_ATTR_NHC_TLV_NNHN 2
extern void bgp_nhc_tlv_add(struct bgp_nhc *nhc, struct bgp_nhc_tlv *tlv);
extern struct bgp_nhc_tlv *bgp_nhc_tlv_find(struct bgp_nhc *nhc, uint16_t code);
extern void bgp_nhc_tlv_free(struct bgp_nhc_tlv *tlv);
extern void bgp_nhc_tlvs_free(struct bgp_nhc_tlv *tlv);
extern void bgp_nhc_free(struct bgp_nhc *bnc);
#endif /* _FRR_BGP_NHC_H */

View file

@ -31,6 +31,7 @@
#include "frrdistance.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_nhc.h"
#include "bgpd/bgp_table.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
@ -12273,6 +12274,40 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
}
}
/* Display NHC attributes */
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NHC))) {
struct bgp_nhc *nhc = bgp_attr_get_nhc(attr);
struct bgp_nhc_tlv *tlv = NULL;
if (nhc) {
for (tlv = nhc->tlvs; tlv; tlv = tlv->next) {
if (tlv->code == BGP_ATTR_NHC_TLV_NNHN) {
json_object *json_nhc_nnhn = NULL;
if (!json_paths)
vty_out(vty, " Next-next Hop Nodes:\n");
else
json_nhc_nnhn = json_object_new_array();
for (i = 0; i < tlv->length; i += IPV4_MAX_BYTELEN) {
if (!json_paths) {
vty_out(vty, " %pI4\n",
(struct in_addr *)&tlv->value[i]);
} else {
json_array_string_addf(json_nhc_nnhn, "%pI4",
(struct in_addr *)&tlv
->value[i]);
}
}
if (json_paths)
json_object_object_add(json_path, "nextNextHopNodes",
json_nhc_nnhn);
}
}
}
}
/* Output some debug about internal state of the dest flags */
if (json_paths) {
if (CHECK_FLAG(bn->flags, BGP_NODE_PROCESS_SCHEDULED))

View file

@ -745,7 +745,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
* attr. */
total_attr_len = bgp_packet_attribute(NULL, peer, s, adv->baa->attr,
&vecarr, NULL, afi, safi, from, NULL,
NULL, 0, 0, 0);
NULL, 0, 0, 0, path);
space_remaining =
STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
@ -1156,7 +1156,7 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp,
stream_putw(s, 0);
total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, &vecarr, &p, afi, safi, from,
NULL, &label, num_labels, addpath_capable,
BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, NULL);
/* Set Total Path Attribute Length. */
stream_putw_at(s, pos, total_attr_len);

View file

@ -2201,6 +2201,7 @@ struct bgp_nlri {
#define BGP_ATTR_AIGP 26
#define BGP_ATTR_LARGE_COMMUNITIES 32
#define BGP_ATTR_OTC 35
#define BGP_ATTR_NHC 39
#define BGP_ATTR_PREFIX_SID 40
#ifdef ENABLE_BGP_VNC_ATTR
#define BGP_ATTR_VNC 255

View file

@ -78,6 +78,7 @@ bgpd_libbgp_a_SOURCES = \
bgpd/bgpd.c \
bgpd/bgp_trace.c \
bgpd/bgp_nhg.c \
bgpd/bgp_nhc.c \
# end
if ENABLE_BGP_VNC
@ -183,6 +184,7 @@ noinst_HEADERS += \
bgpd/rfapi/vnc_export_bgp_p.h \
bgpd/rfapi/vnc_import_bgp_p.h \
bgpd/bgp_vnc_types.h \
bgpd/bgp_nhc.h \
# end
bgpd_bgpd_SOURCES = bgpd/bgp_main.c