mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
Merge 731b0fc70d
into 3dd4d417be
This commit is contained in:
commit
09bf71db2e
379
bgpd/bgp_attr.c
379
bgpd/bgp_attr.c
|
@ -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. */
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -1139,7 +1139,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? */
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
67
bgpd/bgp_nhc.c
Normal 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
57
bgpd/bgp_nhc.h
Normal 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 */
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
10
tests/topotests/bgp_nhc/r1/frr.conf
Normal file
10
tests/topotests/bgp_nhc/r1/frr.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
!
|
||||
int r1-eth0
|
||||
ip address 10.255.0.1/24
|
||||
!
|
||||
router bgp 65001
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 10.255.0.2 timers 1 3
|
||||
neighbor 10.255.0.2 timers connect 1
|
||||
neighbor 10.255.0.2 remote-as external
|
||||
!
|
24
tests/topotests/bgp_nhc/r2/frr.conf
Normal file
24
tests/topotests/bgp_nhc/r2/frr.conf
Normal file
|
@ -0,0 +1,24 @@
|
|||
!
|
||||
int r2-eth0
|
||||
ip address 10.255.0.2/24
|
||||
!
|
||||
int r2-eth1
|
||||
ip address 10.254.0.2/24
|
||||
!
|
||||
router bgp 65002
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp suppress-duplicates
|
||||
bgp bestpath as-path multipath-relax
|
||||
neighbor 10.255.0.1 remote-as external
|
||||
neighbor 10.255.0.1 timers 1 3
|
||||
neighbor 10.255.0.1 timers connect 1
|
||||
neighbor 10.254.0.3 remote-as external
|
||||
neighbor 10.254.0.3 timers 1 3
|
||||
neighbor 10.254.0.3 timers connect 1
|
||||
neighbor 10.254.0.4 remote-as external
|
||||
neighbor 10.254.0.4 timers 1 3
|
||||
neighbor 10.254.0.4 timers connect 1
|
||||
neighbor 10.254.0.5 remote-as external
|
||||
neighbor 10.254.0.5 timers 1 3
|
||||
neighbor 10.254.0.5 timers connect 1
|
||||
!
|
14
tests/topotests/bgp_nhc/r3/frr.conf
Normal file
14
tests/topotests/bgp_nhc/r3/frr.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
!
|
||||
int r3-eth0
|
||||
ip address 10.254.0.3/24
|
||||
!
|
||||
router bgp 65003
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp network import-check
|
||||
neighbor 10.254.0.2 remote-as external
|
||||
neighbor 10.254.0.2 timers 1 3
|
||||
neighbor 10.254.0.2 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
network 10.0.0.1/32
|
||||
exit-address-family
|
||||
!
|
14
tests/topotests/bgp_nhc/r4/frr.conf
Normal file
14
tests/topotests/bgp_nhc/r4/frr.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
!
|
||||
int r4-eth0
|
||||
ip address 10.254.0.4/24
|
||||
!
|
||||
router bgp 65004
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp network import-check
|
||||
neighbor 10.254.0.2 remote-as external
|
||||
neighbor 10.254.0.2 timers 1 3
|
||||
neighbor 10.254.0.2 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
network 10.0.0.1/32
|
||||
exit-address-family
|
||||
!
|
14
tests/topotests/bgp_nhc/r5/frr.conf
Normal file
14
tests/topotests/bgp_nhc/r5/frr.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
!
|
||||
int r5-eth0
|
||||
ip address 10.254.0.5/24
|
||||
!
|
||||
router bgp 65005
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp network import-check
|
||||
neighbor 10.254.0.2 remote-as external
|
||||
neighbor 10.254.0.2 timers 1 3
|
||||
neighbor 10.254.0.2 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
network 10.0.0.1/32
|
||||
exit-address-family
|
||||
!
|
92
tests/topotests/bgp_nhc/test_bgp_nhc.py
Normal file
92
tests/topotests/bgp_nhc/test_bgp_nhc.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
# Copyright (c) 2025 by
|
||||
# Donatas Abraitis <donatas@opensourcerouting.org>
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import pytest
|
||||
import functools
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, get_topogen
|
||||
|
||||
pytestmark = [pytest.mark.bgpd]
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3", "r4", "r5")}
|
||||
tgen = Topogen(topodef, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
router_list = tgen.routers()
|
||||
|
||||
for _, (rname, router) in enumerate(router_list.items(), 1):
|
||||
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
|
||||
|
||||
tgen.start_router()
|
||||
|
||||
|
||||
def teardown_module(mod):
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_bgp_nhc():
|
||||
tgen = get_topogen()
|
||||
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
def _bgp_converge():
|
||||
output = json.loads(r1.vtysh_cmd("show bgp ipv4 json detail"))
|
||||
expected = {
|
||||
"routes": {
|
||||
"10.0.0.1/32": {
|
||||
"paths": [
|
||||
{
|
||||
"aspath": {
|
||||
"string": "65002 65003",
|
||||
},
|
||||
"valid": True,
|
||||
"nextNextHopNodes": [
|
||||
"10.254.0.3",
|
||||
"10.254.0.4",
|
||||
"10.254.0.5",
|
||||
],
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "10.255.0.2",
|
||||
"hostname": "r2",
|
||||
"afi": "ipv4",
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
"totalRoutes": 1,
|
||||
"totalPaths": 1,
|
||||
}
|
||||
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
test_func = functools.partial(
|
||||
_bgp_converge,
|
||||
)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||
assert (
|
||||
result is None
|
||||
), "Can't see Next-next hop Nodes (NHC attribute) for 10.0.0.1/32"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
Loading…
Reference in a new issue