diff options
| -rw-r--r-- | bgpd/bgp_bmp.c | 541 | ||||
| -rw-r--r-- | bgpd/bgp_bmp.h | 10 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 15 | ||||
| -rw-r--r-- | bgpd/bgp_route.h | 9 | ||||
| -rw-r--r-- | bgpd/bgp_trace.h | 3 | ||||
| -rw-r--r-- | doc/developer/bmp.rst | 49 | ||||
| -rw-r--r-- | doc/developer/subdir.am | 1 | ||||
| -rw-r--r-- | doc/user/bmp.rst | 8 | ||||
| -rw-r--r-- | tests/topotests/bgp_bmp/test_bgp_bmp.py | 11 | ||||
| -rw-r--r-- | tests/topotests/lib/bmp_collector/bmp.py | 3 |
10 files changed, 533 insertions, 117 deletions
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 7270802915..e9f912cb18 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -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); - else if (peer->connection->su.sa.sa_family == AF_INET) { + /* 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_put_in_addr(s, &peer->connection->su.sin.sin_addr); - } else { 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); } /* 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; } diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h index ab7463fadc..dadd99eb6d 100644 --- a/bgpd/bgp_bmp.h +++ b/bgpd/bgp_bmp.h @@ -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; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 4ced565d52..b4c97eb2ea 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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) { diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index e9f48ea647..3057a4259a 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -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) diff --git a/bgpd/bgp_trace.h b/bgpd/bgp_trace.h index 964393a9f5..0980073e2b 100644 --- a/bgpd/bgp_trace.h +++ b/bgpd/bgp_trace.h @@ -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) ) ) diff --git a/doc/developer/bmp.rst b/doc/developer/bmp.rst new file mode 100644 index 0000000000..1c0e4b0454 --- /dev/null +++ b/doc/developer/bmp.rst @@ -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. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 0deb0f5da0..652ee4e1af 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -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 \ diff --git a/doc/user/bmp.rst b/doc/user/bmp.rst index 1983995c1f..0f46832059 100644 --- a/doc/user/bmp.rst +++ b/doc/user/bmp.rst @@ -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. diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 65f191b33a..250f1cb90d 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -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__": diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index b07329cd52..57f642aa0e 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -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) |
