Merge pull request #11800 from mxyns/bmp-locribmon

bgpd: BMP Loc-Rib Monitoring (RFC9069) Implementation
This commit is contained in:
Russ White 2023-11-14 08:27:45 -05:00 committed by GitHub
commit 8aae3ea5d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 534 additions and 118 deletions

View File

@ -240,60 +240,115 @@ static void bmp_free(struct bmp *bmp)
XFREE(MTYPE_BMP_CONN, bmp);
}
#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0
#define BMP_PEER_TYPE_RD_INSTANCE 1
#define BMP_PEER_TYPE_LOCAL_INSTANCE 2
#define BMP_PEER_TYPE_LOC_RIB_INSTANCE 3
static inline int bmp_get_peer_distinguisher(struct bmp *bmp, afi_t afi,
uint8_t peer_type,
uint64_t *result_ref)
{
/* remove this check when the other peer types get correct peer dist.
*(RFC7854) impl.
* for now, always return no error and 0 peer distinguisher as before
*/
if (peer_type != BMP_PEER_TYPE_LOC_RIB_INSTANCE)
return (*result_ref = 0);
/* sending vrf_id or rd could be turned into an option at some point */
struct bgp *bgp = bmp->targets->bgp;
/* vrf default => ok, distinguisher 0 */
if (bgp->inst_type == VRF_DEFAULT)
return (*result_ref = 0);
/* use RD if set in VRF config for this AFI */
struct prefix_rd *prd = &bgp->vpn_policy[afi].tovpn_rd;
if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_RD_SET)) {
memcpy(result_ref, prd->val, sizeof(prd->val));
return 0;
}
/* VRF has no id => error => message should be skipped */
if (bgp->vrf_id == VRF_UNKNOWN)
return 1;
/* use VRF id converted to ::vrf_id 64bits format */
*result_ref = ((uint64_t)htonl(bgp->vrf_id)) << 32;
return 0;
}
static void bmp_common_hdr(struct stream *s, uint8_t ver, uint8_t type)
{
stream_putc(s, ver);
stream_putl(s, 0); //dummy message length. will be set later.
stream_putl(s, 0); /* dummy message length. will be set later. */
stream_putc(s, type);
}
static void bmp_per_peer_hdr(struct stream *s, struct peer *peer,
uint8_t flags, const struct timeval *tv)
static void bmp_per_peer_hdr(struct stream *s, struct bgp *bgp,
struct peer *peer, uint8_t flags,
uint8_t peer_type_flag,
uint64_t peer_distinguisher,
const struct timeval *tv)
{
char peer_distinguisher[8];
#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0
#define BMP_PEER_TYPE_RD_INSTANCE 1
#define BMP_PEER_TYPE_LOCAL_INSTANCE 2
#define BMP_PEER_FLAG_V (1 << 7)
#define BMP_PEER_FLAG_L (1 << 6)
#define BMP_PEER_FLAG_A (1 << 5)
bool is_locrib = peer_type_flag == BMP_PEER_TYPE_LOC_RIB_INSTANCE;
/* Peer Type */
stream_putc(s, BMP_PEER_TYPE_GLOBAL_INSTANCE);
stream_putc(s, peer_type_flag);
/* Peer Flags */
if (peer->connection->su.sa.sa_family == AF_INET6)
if (!is_locrib && peer->connection->su.sa.sa_family == AF_INET6)
SET_FLAG(flags, BMP_PEER_FLAG_V);
else
UNSET_FLAG(flags, BMP_PEER_FLAG_V);
stream_putc(s, flags);
/* Peer Distinguisher */
memset (&peer_distinguisher[0], 0, 8);
stream_put(s, &peer_distinguisher[0], 8);
stream_put(s, (uint8_t *)&peer_distinguisher, 8);
/* Peer Address */
if (peer->connection->su.sa.sa_family == AF_INET6)
stream_put(s, &peer->connection->su.sin6.sin6_addr, 16);
/* Set to 0 if it's a LOC-RIB INSTANCE (RFC 9069) or if it's not an
* IPv4/6 address
*/
if (is_locrib || (peer->connection->su.sa.sa_family != AF_INET6 &&
peer->connection->su.sa.sa_family != AF_INET)) {
stream_putl(s, 0);
stream_putl(s, 0);
stream_putl(s, 0);
stream_putl(s, 0);
} else if (peer->connection->su.sa.sa_family == AF_INET6)
stream_put(s, &peer->connection->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
else if (peer->connection->su.sa.sa_family == AF_INET) {
stream_putl(s, 0);
stream_putl(s, 0);
stream_putl(s, 0);
stream_put_in_addr(s, &peer->connection->su.sin.sin_addr);
} else {
stream_putl(s, 0);
stream_putl(s, 0);
stream_putl(s, 0);
stream_putl(s, 0);
}
/* Peer AS */
stream_putl(s, peer->as);
/* set peer ASN but for LOC-RIB INSTANCE (RFC 9069) put the local bgp
* ASN if available or 0
*/
as_t asn = !is_locrib ? peer->as : bgp ? bgp->as : 0L;
stream_putl(s, asn);
/* Peer BGP ID */
stream_put_in_addr(s, &peer->remote_id);
/* set router-id but for LOC-RIB INSTANCE (RFC 9069) put the instance
* router-id if available or 0
*/
struct in_addr *bgp_id =
!is_locrib ? &peer->remote_id : bgp ? &bgp->router_id : NULL;
stream_put_in_addr(s, bgp_id);
/* Timestamp */
if (tv) {
@ -314,11 +369,26 @@ static void bmp_put_info_tlv(struct stream *s, uint16_t type,
stream_put(s, string, len);
}
static void __attribute__((unused))
bmp_put_vrftablename_info_tlv(struct stream *s, struct bmp *bmp)
{
#define BMP_INFO_TYPE_VRFTABLENAME 3
const char *vrftablename = "global";
if (bmp->targets->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
struct vrf *vrf = vrf_lookup_by_id(bmp->targets->bgp->vrf_id);
vrftablename = vrf ? vrf->name : NULL;
}
if (vrftablename != NULL)
bmp_put_info_tlv(s, BMP_INFO_TYPE_VRFTABLENAME, vrftablename);
}
static int bmp_send_initiation(struct bmp *bmp)
{
int len;
struct stream *s;
s = stream_new(BGP_MAX_PACKET_SIZE);
struct stream *s = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION);
#define BMP_INFO_TYPE_SYSDESCR 1
@ -328,7 +398,7 @@ static int bmp_send_initiation(struct bmp *bmp)
bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get());
len = stream_get_endp(s);
stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set.
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
pullwr_write_stream(bmp->pullwr, s);
stream_free(s);
@ -375,7 +445,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
bmp_common_hdr(s, BMP_VERSION_3,
BMP_TYPE_PEER_UP_NOTIFICATION);
bmp_per_peer_hdr(s, peer, 0, &uptime_real);
bmp_per_peer_hdr(s, peer->bgp, peer, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE, 0,
&uptime_real);
/* Local Address (16 bytes) */
if (peer->su_local->sa.sa_family == AF_INET6)
@ -428,7 +500,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
bmp_common_hdr(s, BMP_VERSION_3,
BMP_TYPE_PEER_DOWN_NOTIFICATION);
bmp_per_peer_hdr(s, peer, 0, &uptime_real);
bmp_per_peer_hdr(s, peer->bgp, peer, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE, 0,
&uptime_real);
type_pos = stream_get_endp(s);
stream_putc(s, 0); /* placeholder for down reason */
@ -460,7 +534,7 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
}
len = stream_get_endp(s);
stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set.
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
return s;
}
@ -618,7 +692,8 @@ static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr)
s = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
bmp_per_peer_hdr(s, bmp->targets->bgp->peer_self, 0, &tv);
bmp_per_peer_hdr(s, bmp->targets->bgp, bmp->targets->bgp->peer_self, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &tv);
stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO);
stream_putw(s, 2);
@ -656,7 +731,8 @@ static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr)
s = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
bmp_per_peer_hdr(s, peer, 0, &bmq->tv);
bmp_per_peer_hdr(s, bmp->targets->bgp, peer, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &bmq->tv);
/* BMP Mirror TLV. */
stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE);
@ -763,7 +839,8 @@ static int bmp_peer_backward(struct peer *peer)
return 0;
}
static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags)
static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags,
uint8_t peer_type_flag)
{
struct peer *peer;
struct listnode *node;
@ -771,7 +848,7 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags)
iana_afi_t pkt_afi = IANA_AFI_IPV4;
iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
frrtrace(3, frr_bgp, bmp_eor, afi, safi, flags);
frrtrace(3, frr_bgp, bmp_eor, afi, safi, flags, peer_type_flag);
s = stream_new(BGP_MAX_PACKET_SIZE);
@ -803,11 +880,22 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags)
if (!peer->afc_nego[afi][safi])
continue;
uint64_t peer_distinguisher = 0;
/* skip this message if peer distinguisher is not available */
if (bmp_get_peer_distinguisher(bmp, afi, peer_type_flag,
&peer_distinguisher)) {
zlog_warn(
"skipping bmp message for reason: can't get peer distinguisher");
continue;
}
s2 = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(s2, BMP_VERSION_3,
BMP_TYPE_ROUTE_MONITORING);
bmp_per_peer_hdr(s2, peer, flags, NULL);
bmp_per_peer_hdr(s2, bmp->targets->bgp, peer, flags,
peer_type_flag, peer_distinguisher, NULL);
stream_putl_at(s2, BMP_LENGTH_POS,
stream_get_endp(s) + stream_get_endp(s2));
@ -912,14 +1000,23 @@ static struct stream *bmp_withdraw(const struct prefix *p,
}
static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
const struct prefix *p, struct prefix_rd *prd,
struct attr *attr, afi_t afi, safi_t safi,
time_t uptime)
uint8_t peer_type_flag, const struct prefix *p,
struct prefix_rd *prd, struct attr *attr, afi_t afi,
safi_t safi, time_t uptime)
{
struct stream *hdr, *msg;
struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 };
struct timeval uptime_real;
uint64_t peer_distinguisher = 0;
/* skip this message if peer distinguisher is not available */
if (bmp_get_peer_distinguisher(bmp, afi, peer_type_flag,
&peer_distinguisher)) {
zlog_warn(
"skipping bmp message for reason: can't get peer distinguisher");
return;
}
monotime_to_realtime(&tv, &uptime_real);
if (attr)
msg = bmp_update(p, prd, peer, attr, afi, safi);
@ -928,7 +1025,9 @@ static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
hdr = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING);
bmp_per_peer_hdr(hdr, peer, flags, &uptime_real);
bmp_per_peer_hdr(hdr, bmp->targets->bgp, peer, flags, peer_type_flag,
peer_distinguisher,
uptime == (time_t)(-1L) ? NULL : &uptime_real);
stream_putl_at(hdr, BMP_LENGTH_POS,
stream_get_endp(hdr) + stream_get_endp(msg));
@ -1039,8 +1138,13 @@ afibreak:
zlog_info("bmp[%s] %s %s table completed (EoR)",
bmp->remote, afi2str(afi),
safi2str(safi));
bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L);
bmp_eor(bmp, afi, safi, 0);
bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L,
BMP_PEER_TYPE_GLOBAL_INSTANCE);
bmp_eor(bmp, afi, safi, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE);
bmp_eor(bmp, afi, safi, 0,
BMP_PEER_TYPE_LOC_RIB_INSTANCE);
bmp->afistate[afi][safi] = BMP_AFI_LIVE;
bmp->syncafi = AFI_MAX;
@ -1051,10 +1155,16 @@ afibreak:
prefix_copy(&bmp->syncpos, bgp_dest_get_prefix(bn));
}
if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) {
if (CHECK_FLAG(bmp->targets->afimon[afi][safi],
BMP_MON_POSTPOLICY) ||
CHECK_FLAG(bmp->targets->afimon[afi][safi],
BMP_MON_LOC_RIB)) {
for (bpiter = bgp_dest_get_bgp_path_info(bn); bpiter;
bpiter = bpiter->next) {
if (!CHECK_FLAG(bpiter->flags, BGP_PATH_VALID))
if (!CHECK_FLAG(bpiter->flags,
BGP_PATH_VALID) &&
!CHECK_FLAG(bpiter->flags,
BGP_PATH_SELECTED))
continue;
if (bpiter->peer->qobj_node.nid
<= bmp->syncpeerid)
@ -1065,7 +1175,8 @@ afibreak:
bpi = bpiter;
}
}
if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) {
if (CHECK_FLAG(bmp->targets->afimon[afi][safi],
BMP_MON_PREPOLICY)) {
for (adjiter = bn->adj_in; adjiter;
adjiter = adjiter->next) {
if (adjiter->peer->qobj_node.nid
@ -1103,12 +1214,23 @@ afibreak:
(safi == SAFI_MPLS_VPN))
prd = (struct prefix_rd *)bgp_dest_get_prefix(bmp->syncrdpos);
if (bpi)
bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bn_p, prd,
bpi->attr, afi, safi, bpi->uptime);
if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_SELECTED) &&
CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_LOC_RIB)) {
bmp_monitor(bmp, bpi->peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE,
bn_p, prd, bpi->attr, afi, safi,
bpi && bpi->extra ? bpi->extra->bgp_rib_uptime
: (time_t)(-1L));
}
if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_VALID) &&
CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY))
bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L,
BMP_PEER_TYPE_GLOBAL_INSTANCE, bn_p, prd, bpi->attr,
afi, safi, bpi->uptime);
if (adjin)
bmp_monitor(bmp, adjin->peer, 0, bn_p, prd, adjin->attr, afi,
safi, adjin->uptime);
bmp_monitor(bmp, adjin->peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE,
bn_p, prd, adjin->attr, afi, safi, adjin->uptime);
if (bn)
bgp_dest_unlock_node(bn);
@ -1116,24 +1238,124 @@ afibreak:
return true;
}
static struct bmp_queue_entry *bmp_pull(struct bmp *bmp)
static struct bmp_queue_entry *
bmp_pull_from_queue(struct bmp_qlist_head *list, struct bmp_qhash_head *hash,
struct bmp_queue_entry **queuepos_ptr)
{
struct bmp_queue_entry *bqe;
bqe = bmp->queuepos;
bqe = *queuepos_ptr;
if (!bqe)
return NULL;
bmp->queuepos = bmp_qlist_next(&bmp->targets->updlist, bqe);
*queuepos_ptr = bmp_qlist_next(list, bqe);
bqe->refcount--;
if (!bqe->refcount) {
bmp_qhash_del(&bmp->targets->updhash, bqe);
bmp_qlist_del(&bmp->targets->updlist, bqe);
bmp_qhash_del(hash, bqe);
bmp_qlist_del(list, bqe);
}
return bqe;
}
static inline struct bmp_queue_entry *bmp_pull(struct bmp *bmp)
{
return bmp_pull_from_queue(&bmp->targets->updlist,
&bmp->targets->updhash, &bmp->queuepos);
}
static inline struct bmp_queue_entry *bmp_pull_locrib(struct bmp *bmp)
{
return bmp_pull_from_queue(&bmp->targets->locupdlist,
&bmp->targets->locupdhash,
&bmp->locrib_queuepos);
}
/* TODO BMP_MON_LOCRIB find a way to merge properly this function with
* bmp_wrqueue or abstract it if possible
*/
static bool bmp_wrqueue_locrib(struct bmp *bmp, struct pullwr *pullwr)
{
struct bmp_queue_entry *bqe;
struct peer *peer;
struct bgp_dest *bn = NULL;
bool written = false;
bqe = bmp_pull_locrib(bmp);
if (!bqe)
return false;
afi_t afi = bqe->afi;
safi_t safi = bqe->safi;
if (!CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_LOC_RIB))
goto out;
switch (bmp->afistate[afi][safi]) {
case BMP_AFI_INACTIVE:
case BMP_AFI_NEEDSYNC:
goto out;
case BMP_AFI_SYNC:
if (prefix_cmp(&bqe->p, &bmp->syncpos) <= 0)
/* currently syncing but have already passed this
* prefix => send it.
*/
break;
/* currently syncing & haven't reached this prefix yet
* => it'll be sent as part of the table sync, no need here
*/
goto out;
case BMP_AFI_LIVE:
break;
}
peer = QOBJ_GET_TYPESAFE(bqe->peerid, peer);
if (!peer) {
/* skipping queued item for deleted peer
*/
goto out;
}
if (peer != bmp->targets->bgp->peer_self && !peer_established(peer->connection)) {
/* peer is neither self, nor established
*/
goto out;
}
bool is_vpn = (bqe->afi == AFI_L2VPN && bqe->safi == SAFI_EVPN) ||
(bqe->safi == SAFI_MPLS_VPN);
struct prefix_rd *prd = is_vpn ? &bqe->rd : NULL;
bn = bgp_safi_node_lookup(bmp->targets->bgp->rib[afi][safi], safi,
&bqe->p, prd);
struct bgp_path_info *bpi;
for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
if (!CHECK_FLAG(bpi->flags, BGP_PATH_SELECTED))
continue;
if (bpi->peer == peer)
break;
}
bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, &bqe->p, prd,
bpi ? bpi->attr : NULL, afi, safi,
bpi && bpi->extra ? bpi->extra->bgp_rib_uptime
: (time_t)(-1L));
written = true;
out:
if (!bqe->refcount)
XFREE(MTYPE_BMP_QUEUE, bqe);
if (bn)
bgp_dest_unlock_node(bn);
return written;
}
static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
{
struct bmp_queue_entry *bqe;
@ -1180,11 +1402,10 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
bn = bgp_safi_node_lookup(bmp->targets->bgp->rib[afi][safi], safi,
&bqe->p, prd);
if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) {
if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY)) {
struct bgp_path_info *bpi;
for (bpi = bn ? bgp_dest_get_bgp_path_info(bn) : NULL; bpi;
for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
bpi = bpi->next) {
if (!CHECK_FLAG(bpi->flags, BGP_PATH_VALID))
continue;
@ -1192,13 +1413,14 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
break;
}
bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd,
bmp_monitor(bmp, peer, BMP_PEER_FLAG_L,
BMP_PEER_TYPE_GLOBAL_INSTANCE, &bqe->p, prd,
bpi ? bpi->attr : NULL, afi, safi,
bpi ? bpi->uptime : monotime(NULL));
written = true;
}
if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) {
if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_PREPOLICY)) {
struct bgp_adj_in *adjin;
for (adjin = bn ? bn->adj_in : NULL; adjin;
@ -1206,8 +1428,8 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
if (adjin->peer == peer)
break;
}
bmp_monitor(bmp, peer, 0, &bqe->p, prd,
adjin ? adjin->attr : NULL, afi, safi,
bmp_monitor(bmp, peer, 0, BMP_PEER_TYPE_GLOBAL_INSTANCE,
&bqe->p, prd, adjin ? adjin->attr : NULL, afi, safi,
adjin ? adjin->uptime : monotime(NULL));
written = true;
}
@ -1235,6 +1457,8 @@ static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr)
break;
if (bmp_wrqueue(bmp, pullwr))
break;
if (bmp_wrqueue_locrib(bmp, pullwr))
break;
if (bmp_wrsync(bmp, pullwr))
break;
break;
@ -1253,16 +1477,17 @@ static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof)
bmp_free(bmp);
}
static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi,
safi_t safi, struct bgp_dest *bn, struct peer *peer)
static struct bmp_queue_entry *
bmp_process_one(struct bmp_targets *bt, struct bmp_qhash_head *updhash,
struct bmp_qlist_head *updlist, struct bgp *bgp, afi_t afi,
safi_t safi, struct bgp_dest *bn, struct peer *peer)
{
struct bmp *bmp;
struct bmp_queue_entry *bqe, bqeref;
size_t refcount;
refcount = bmp_session_count(&bt->sessions);
if (refcount == 0)
return;
return NULL;
memset(&bqeref, 0, sizeof(bqeref));
prefix_copy(&bqeref.p, bgp_dest_get_prefix(bn));
@ -1275,26 +1500,28 @@ static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi,
prefix_copy(&bqeref.rd,
(struct prefix_rd *)bgp_dest_get_prefix(bn->pdest));
bqe = bmp_qhash_find(&bt->updhash, &bqeref);
bqe = bmp_qhash_find(updhash, &bqeref);
if (bqe) {
if (bqe->refcount >= refcount)
/* nothing to do here */
return;
return NULL;
bmp_qlist_del(&bt->updlist, bqe);
bmp_qlist_del(updlist, bqe);
} else {
bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe));
memcpy(bqe, &bqeref, sizeof(*bqe));
bmp_qhash_add(&bt->updhash, bqe);
bmp_qhash_add(updhash, bqe);
}
bqe->refcount = refcount;
bmp_qlist_add_tail(&bt->updlist, bqe);
bmp_qlist_add_tail(updlist, bqe);
frr_each (bmp_session, &bt->sessions, bmp)
if (!bmp->queuepos)
bmp->queuepos = bqe;
return bqe;
/* need to update correct queue pos for all sessions of the target after
* a call to this function
*/
}
static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi,
@ -1316,12 +1543,26 @@ static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi,
return 0;
frr_each(bmp_targets, &bmpbgp->targets, bt) {
if (!bt->afimon[afi][safi])
/* check if any monitoring is enabled (ignoring loc-rib since it
* uses another hook & queue
*/
if (!CHECK_FLAG(bt->afimon[afi][safi], ~BMP_MON_LOC_RIB))
continue;
bmp_process_one(bt, bgp, afi, safi, bn, peer);
struct bmp_queue_entry *last_item =
bmp_process_one(bt, &bt->updhash, &bt->updlist, bgp,
afi, safi, bn, peer);
/* if bmp_process_one returns NULL
* we don't have anything to do next
*/
if (!last_item)
continue;
frr_each(bmp_session, &bt->sessions, bmp) {
if (!bmp->queuepos)
bmp->queuepos = last_item;
pullwr_bump(bmp->pullwr);
}
}
@ -1360,7 +1601,8 @@ static void bmp_stats(struct event *thread)
s = stream_new(BGP_MAX_PACKET_SIZE);
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT);
bmp_per_peer_hdr(s, peer, 0, &tv);
bmp_per_peer_hdr(s, bt->bgp, peer, 0,
BMP_PEER_TYPE_GLOBAL_INSTANCE, 0, &tv);
count_pos = stream_get_endp(s);
stream_putl(s, 0);
@ -1530,6 +1772,9 @@ static void bmp_close(struct bmp *bmp)
while ((bqe = bmp_pull(bmp)))
if (!bqe->refcount)
XFREE(MTYPE_BMP_QUEUE, bqe);
while ((bqe = bmp_pull_locrib(bmp)))
if (!bqe->refcount)
XFREE(MTYPE_BMP_QUEUE, bqe);
EVENT_OFF(bmp->t_read);
pullwr_del(bmp->pullwr);
@ -1633,6 +1878,8 @@ static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name)
bmp_session_init(&bt->sessions);
bmp_qhash_init(&bt->updhash);
bmp_qlist_init(&bt->updlist);
bmp_qhash_init(&bt->locupdhash);
bmp_qlist_init(&bt->locupdlist);
bmp_actives_init(&bt->actives);
bmp_listeners_init(&bt->listeners);
@ -1663,6 +1910,8 @@ static void bmp_targets_put(struct bmp_targets *bt)
bmp_actives_fini(&bt->actives);
bmp_qhash_fini(&bt->updhash);
bmp_qlist_fini(&bt->updlist);
bmp_qhash_fini(&bt->locupdhash);
bmp_qlist_fini(&bt->locupdlist);
XFREE(MTYPE_BMP_ACLNAME, bt->acl_name);
XFREE(MTYPE_BMP_ACLNAME, bt->acl6_name);
@ -2206,21 +2455,17 @@ DEFPY(bmp_stats_cfg,
return CMD_SUCCESS;
}
DEFPY(bmp_monitor_cfg,
bmp_monitor_cmd,
"[no] bmp monitor <ipv4|ipv6|l2vpn> <unicast|multicast|evpn|vpn> <pre-policy|post-policy>$policy",
NO_STR
BMP_STR
"Send BMP route monitoring messages\n"
BGP_AF_STR
BGP_AF_STR
BGP_AF_STR
BGP_AF_STR
BGP_AF_STR
BGP_AF_STR
BGP_AF_STR
#define BMP_POLICY_IS_LOCRIB(str) ((str)[0] == 'l') /* __l__oc-rib */
#define BMP_POLICY_IS_PRE(str) ((str)[1] == 'r') /* p__r__e-policy */
DEFPY(bmp_monitor_cfg, bmp_monitor_cmd,
"[no] bmp monitor <ipv4|ipv6|l2vpn> <unicast|multicast|evpn|vpn> <pre-policy|post-policy|loc-rib>$policy",
NO_STR BMP_STR
"Send BMP route monitoring messages\n" BGP_AF_STR BGP_AF_STR BGP_AF_STR
BGP_AF_STR BGP_AF_STR BGP_AF_STR BGP_AF_STR
"Send state before policy and filter processing\n"
"Send state with policy and filters applied\n")
"Send state with policy and filters applied\n"
"Send state after decision process is applied\n")
{
int index = 0;
uint8_t flag, prev;
@ -2233,7 +2478,9 @@ DEFPY(bmp_monitor_cfg,
argv_find_and_parse_afi(argv, argc, &index, &afi);
argv_find_and_parse_safi(argv, argc, &index, &safi);
if (policy[1] == 'r')
if (BMP_POLICY_IS_LOCRIB(policy))
flag = BMP_MON_LOC_RIB;
else if (BMP_POLICY_IS_PRE(policy))
flag = BMP_MON_PREPOLICY;
else
flag = BMP_MON_POSTPOLICY;
@ -2364,23 +2611,31 @@ DEFPY(show_bmp,
safi_t safi;
FOREACH_AFI_SAFI (afi, safi) {
const char *str = NULL;
switch (bt->afimon[afi][safi]) {
case BMP_MON_PREPOLICY:
str = "pre-policy";
break;
case BMP_MON_POSTPOLICY:
str = "post-policy";
break;
case BMP_MON_PREPOLICY | BMP_MON_POSTPOLICY:
str = "pre-policy and post-policy";
break;
}
if (!str)
uint8_t afimon_flag = bt->afimon[afi][safi];
if (!afimon_flag)
continue;
vty_out(vty, " Route Monitoring %s %s %s\n",
afi2str(afi), safi2str(safi), str);
const char *pre_str =
CHECK_FLAG(afimon_flag,
BMP_MON_PREPOLICY)
? "pre-policy "
: "";
const char *post_str =
CHECK_FLAG(afimon_flag,
BMP_MON_POSTPOLICY)
? "post-policy "
: "";
const char *locrib_str =
CHECK_FLAG(afimon_flag, BMP_MON_LOC_RIB)
? "loc-rib"
: "";
vty_out(vty,
" Route Monitoring %s %s %s%s%s\n",
afi2str(afi), safi2str(safi), pre_str,
post_str, locrib_str);
}
vty_out(vty, " Listeners:\n");
@ -2500,13 +2755,18 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty)
vty_out(vty, " bmp mirror\n");
FOREACH_AFI_SAFI (afi, safi) {
if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY)
if (CHECK_FLAG(bt->afimon[afi][safi],
BMP_MON_PREPOLICY))
vty_out(vty, " bmp monitor %s %s pre-policy\n",
afi2str_lower(afi), safi2str(safi));
if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY)
if (CHECK_FLAG(bt->afimon[afi][safi],
BMP_MON_POSTPOLICY))
vty_out(vty,
" bmp monitor %s %s post-policy\n",
afi2str_lower(afi), safi2str(safi));
if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB))
vty_out(vty, " bmp monitor %s %s loc-rib\n",
afi2str(afi), safi2str(safi));
}
frr_each (bmp_listeners, &bt->listeners, bl)
vty_out(vty, " \n bmp listener %pSU port %d\n",
@ -2555,6 +2815,76 @@ static int bgp_bmp_init(struct event_loop *tm)
return 0;
}
static int bmp_route_update(struct bgp *bgp, afi_t afi, safi_t safi,
struct bgp_dest *bn,
struct bgp_path_info *old_route,
struct bgp_path_info *new_route)
{
bool is_locribmon_enabled = false;
bool is_withdraw = old_route && !new_route;
struct bgp_path_info *updated_route =
is_withdraw ? old_route : new_route;
/* this should never happen */
if (!updated_route) {
zlog_warn("%s: no updated route found!", __func__);
return 0;
}
struct bmp_bgp *bmpbgp = bmp_bgp_get(bgp);
struct peer *peer = updated_route->peer;
struct bmp_targets *bt;
struct bmp *bmp;
frr_each (bmp_targets, &bmpbgp->targets, bt) {
if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) {
is_locribmon_enabled = true;
break;
}
}
if (!is_locribmon_enabled)
return 0;
/* route is not installed in locrib anymore and rib uptime was saved */
if (old_route && old_route->extra)
bgp_path_info_extra_get(old_route)->bgp_rib_uptime =
(time_t)(-1L);
/* route is installed in locrib from now on so
* save rib uptime in bgp_path_info_extra
*/
if (new_route)
bgp_path_info_extra_get(new_route)->bgp_rib_uptime =
monotime(NULL);
frr_each (bmp_targets, &bmpbgp->targets, bt) {
if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) {
struct bmp_queue_entry *last_item = bmp_process_one(
bt, &bt->locupdhash, &bt->locupdlist, bgp, afi,
safi, bn, peer);
/* if bmp_process_one returns NULL
* we don't have anything to do next
*/
if (!last_item)
continue;
frr_each (bmp_session, &bt->sessions, bmp) {
if (!bmp->locrib_queuepos)
bmp->locrib_queuepos = last_item;
pullwr_bump(bmp->pullwr);
};
}
};
return 0;
}
static int bgp_bmp_module_init(void)
{
hook_register(bgp_packet_dump, bmp_mirror_packet);
@ -2565,6 +2895,7 @@ static int bgp_bmp_module_init(void)
hook_register(bgp_inst_config_write, bmp_config_write);
hook_register(bgp_inst_delete, bmp_bgp_del);
hook_register(frr_late_init, bgp_bmp_init);
hook_register(bgp_route_update, bmp_route_update);
return 0;
}

View File

@ -124,6 +124,7 @@ struct bmp {
* ahead we need to make sure that refcount is decremented. Also, on
* disconnects we need to walk the queue and drop our reference.
*/
struct bmp_queue_entry *locrib_queuepos;
struct bmp_queue_entry *queuepos;
struct bmp_mirrorq *mirrorpos;
bool mirror_lost;
@ -215,12 +216,14 @@ struct bmp_targets {
int stat_msec;
/* only supporting:
* - IPv4 / unicast & multicast
* - IPv6 / unicast & multicast
* - IPv4 / unicast & multicast & VPN
* - IPv6 / unicast & multicast & VPN
* - L2VPN / EVPN
*/
#define BMP_MON_PREPOLICY (1 << 0)
#define BMP_MON_POSTPOLICY (1 << 1)
#define BMP_MON_LOC_RIB (1 << 2)
uint8_t afimon[AFI_MAX][SAFI_MAX];
bool mirror;
@ -232,6 +235,9 @@ struct bmp_targets {
struct bmp_qhash_head updhash;
struct bmp_qlist_head updlist;
struct bmp_qhash_head locupdhash;
struct bmp_qlist_head locupdlist;
uint64_t cnt_accept, cnt_aclrefused;
QOBJ_FIELDS;

View File

@ -87,6 +87,11 @@ DEFINE_HOOK(bgp_rpki_prefix_status,
const struct prefix *prefix),
(peer, attr, prefix));
DEFINE_HOOK(bgp_route_update,
(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *bn,
struct bgp_path_info *old_route, struct bgp_path_info *new_route),
(bgp, afi, safi, bn, old_route, new_route));
/* Extern from bgp_dump.c */
extern const char *bgp_origin_str[];
extern const char *bgp_origin_long_str[];
@ -3437,6 +3442,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
&bgp->t_rmap_def_originate_eval);
}
/* TODO BMP insert rib update hook */
if (old_select)
bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
if (new_select) {
@ -3449,6 +3455,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG);
}
/* call bmp hook for loc-rib route update / withdraw after flags were
* set
*/
if (old_select || new_select) {
hook_call(bgp_route_update, bgp, afi, safi, dest, old_select,
new_select);
}
#ifdef ENABLE_BGP_VNC
if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
if (old_select != new_select) {

View File

@ -211,6 +211,9 @@ struct bgp_path_info_extra {
mpls_label_t label[BGP_MAX_LABELS];
uint32_t num_labels;
/* timestamp of the rib installation */
time_t bgp_rib_uptime;
/*For EVPN*/
struct bgp_path_info_extra_evpn *evpn;
@ -674,6 +677,12 @@ DECLARE_HOOK(bgp_process,
struct peer *peer, bool withdraw),
(bgp, afi, safi, bn, peer, withdraw));
/* called when a route is updated in the rib */
DECLARE_HOOK(bgp_route_update,
(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_dest *bn,
struct bgp_path_info *old_route, struct bgp_path_info *new_route),
(bgp, afi, safi, bn, old_route, new_route));
/* BGP show options */
#define BGP_SHOW_OPT_JSON (1 << 0)
#define BGP_SHOW_OPT_WIDE (1 << 1)

View File

@ -135,11 +135,12 @@ TRACEPOINT_LOGLEVEL(frr_bgp, bmp_mirror_packet, TRACE_INFO)
TRACEPOINT_EVENT(
frr_bgp,
bmp_eor,
TP_ARGS(afi_t, afi, safi_t, safi, uint8_t, flags),
TP_ARGS(afi_t, afi, safi_t, safi, uint8_t, flags, peer_type_flag),
TP_FIELDS(
ctf_integer(afi_t, afi, afi)
ctf_integer(safi_t, safi, safi)
ctf_integer(uint8_t, flags, flags)
ctf_integer(uint8_t, peer_type_flag, peer_type_flag)
)
)

49
doc/developer/bmp.rst Normal file
View File

@ -0,0 +1,49 @@
.. _bmp:
***
BMP
***
RFC 7854
========
Missing features (non exhaustive):
- Per-Peer Header
- Peer Type Flag
- Peer Distingsher
- Peer Up
- Reason codes (according to TODO comments in code)
Peer Type Flag and Peer Distinguisher can be implemented easily using RFC 9069's base code.
RFC 9069
========
Everything that isn't listed here is implemented and should be working.
Missing features (should be exhaustive):
- Per-Peer Header
- Timestamp
- set to 0
- value is now saved `struct bgp_path_info -> locrib_uptime`
- needs testing
- Peer Up/Down
- VRF/Table Name TLV
- code for TLV exists
- need better RFC understanding
- Peer Down Only
- Reason code (bc not supported in RFC 7854 either)
- Statistics Report
- Stat Type = 8: (64-bit Gauge) Number of routes in Loc-RIB.
- Stat Type = 10: Number of routes in per-AFI/SAFI Loc-RIB. The value is
structured as: 2-byte AFI, 1-byte SAFI, followed by a 64-bit Gauge.

View File

@ -5,6 +5,7 @@
dev_RSTFILES = \
doc/developer/bgp-typecodes.rst \
doc/developer/bgpd.rst \
doc/developer/bmp.rst \
doc/developer/building-frr-for-alpine.rst \
doc/developer/building-frr-for-archlinux.rst \
doc/developer/building-frr-for-centos6.rst \

View File

@ -36,8 +36,8 @@ The `BMP` implementation in FRR has the following properties:
successfully. OPEN messages for failed sessions cannot currently be
mirrored.
- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast and
multicast SAFIs. Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not
- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast, multicast,
EVPN and VPN SAFIs. Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not
currently supported.
- monitoring peers that have BGP **add-path** enabled on the session will
@ -146,10 +146,10 @@ associated with a particular ``bmp targets``:
Send BMP Statistics (counter) messages at the specified interval (in
milliseconds.)
.. clicmd:: bmp monitor AFI SAFI <pre-policy|post-policy>
.. clicmd:: bmp monitor AFI SAFI <pre-policy|post-policy|loc-rib>
Perform Route Monitoring for the specified AFI and SAFI. Only IPv4 and
IPv6 are currently valid for AFI. SAFI valid values are currently
IPv6 are currently valid for AFI. SAFI valid values are currently
unicast, multicast, evpn and vpn.
Other AFI/SAFI combinations may be added in the future.

View File

@ -49,6 +49,7 @@ SEQ = 0
PRE_POLICY = "pre-policy"
POST_POLICY = "post-policy"
LOC_RIB = "loc-rib"
def build_topo(tgen):
@ -120,7 +121,7 @@ def get_bmp_messages():
return messages
def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy):
def check_for_prefixes(expected_prefixes, bmp_log_type, policy):
"""
Check for the presence of the given prefixes in the BMP server logs with
the given message type and the set policy.
@ -138,7 +139,7 @@ def check_for_prefixes(expected_prefixes, bmp_log_type, post_policy):
if "ip_prefix" in m.keys()
and "bmp_log_type" in m.keys()
and m["bmp_log_type"] == bmp_log_type
and m["post_policy"] == post_policy
and m["policy"] == policy
]
# check for prefixes
@ -202,7 +203,7 @@ def unicast_prefixes(policy):
logger.info("checking for updated prefixes")
# check
test_func = partial(check_for_prefixes, prefixes, "update", policy == POST_POLICY)
test_func = partial(check_for_prefixes, prefixes, "update", policy)
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
assert success, "Checking the updated prefixes has been failed !."
@ -210,7 +211,7 @@ def unicast_prefixes(policy):
configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False)
logger.info("checking for withdrawed prefxies")
# check
test_func = partial(check_for_prefixes, prefixes, "withdraw", policy == POST_POLICY)
test_func = partial(check_for_prefixes, prefixes, "withdraw", policy)
success, _ = topotest.run_and_expect(test_func, True, wait=0.5)
assert success, "Checking the withdrawed prefixes has been failed !."
@ -239,6 +240,8 @@ def test_bmp_bgp_unicast():
unicast_prefixes(PRE_POLICY)
logger.info("*** Unicast prefixes post-policy logging ***")
unicast_prefixes(POST_POLICY)
logger.info("*** Unicast prefixes loc-rib logging ***")
unicast_prefixes(LOC_RIB)
if __name__ == "__main__":

View File

@ -252,6 +252,7 @@ class BMPPerPeerMessage:
if peer_type == 0x03:
msg['is_filtered'] = bool(peer_flags & IS_FILTERED)
msg['policy'] = 'loc-rib'
else:
# peer_flags = 0x0000 0000
# ipv6, post-policy, as-path, adj-rib-out, reserverdx4
@ -259,7 +260,7 @@ class BMPPerPeerMessage:
is_as_path = bool(peer_flags & IS_AS_PATH)
is_post_policy = bool(peer_flags & IS_POST_POLICY)
is_ipv6 = bool(peer_flags & IS_IPV6)
msg['post_policy'] = is_post_policy
msg['policy'] = 'post-policy' if is_post_policy else 'pre-policy'
msg['ipv6'] = is_ipv6
msg['peer_ip'] = bin2str_ipaddress(peer_address, is_ipv6)