2025-02-05 10:03:58 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/* BMP support.
|
|
|
|
* Copyright (C) 2018 Yasuhiro Ohara
|
|
|
|
* Copyright (C) 2019 David Lamparter for NetDEF, Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#include "log.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "sockunion.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "prefix.h"
|
|
|
|
#include "frrevent.h"
|
|
|
|
#include "linklist.h"
|
|
|
|
#include "queue.h"
|
|
|
|
#include "pullwr.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "filter.h"
|
|
|
|
#include "lib_errors.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "libfrr.h"
|
|
|
|
#include "lib/version.h"
|
|
|
|
#include "jhash.h"
|
|
|
|
#include "termtable.h"
|
|
|
|
|
|
|
|
#include "bgpd/bgp_table.h"
|
|
|
|
#include "bgpd/bgpd.h"
|
|
|
|
#include "bgpd/bgp_route.h"
|
2025-03-12 10:26:12 +01:00
|
|
|
#include "bgpd/bgp_nht.h"
|
2025-02-05 10:03:58 +01:00
|
|
|
#include "bgpd/bgp_attr.h"
|
|
|
|
#include "bgpd/bgp_dump.h"
|
|
|
|
#include "bgpd/bgp_errors.h"
|
|
|
|
#include "bgpd/bgp_packet.h"
|
|
|
|
#include "bgpd/bgp_bmp.h"
|
|
|
|
#include "bgpd/bgp_fsm.h"
|
|
|
|
#include "bgpd/bgp_updgrp.h"
|
|
|
|
#include "bgpd/bgp_vty.h"
|
|
|
|
#include "bgpd/bgp_trace.h"
|
|
|
|
#include "bgpd/bgp_network.h"
|
|
|
|
#include "bgpd/bgp_label.h"
|
2025-03-12 10:26:12 +01:00
|
|
|
#include "bgpd/bgp_open.h"
|
|
|
|
#include "bgpd/bgp_aspath.h"
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
static void bmp_close(struct bmp *bmp);
|
|
|
|
static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp);
|
|
|
|
static void bmp_targets_put(struct bmp_targets *bt);
|
|
|
|
static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid);
|
|
|
|
static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer);
|
|
|
|
static void bmp_active_disconnected(struct bmp_active *ba);
|
|
|
|
static void bmp_active_put(struct bmp_active *ba);
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_route_update_bgpbmp(struct bmp_targets *bt, afi_t afi, safi_t safi,
|
|
|
|
struct bgp_dest *bn, struct bgp_path_info *old_route,
|
|
|
|
struct bgp_path_info *new_route);
|
|
|
|
static void bmp_send_all_bgp(struct peer *peer, bool down);
|
|
|
|
static struct bmp_imported_bgp *bmp_imported_bgp_find(struct bmp_targets *bt, char *name);
|
|
|
|
static void bmp_stats_per_instance(struct bgp *bgp, struct bmp_targets *bt);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
DEFINE_MGROUP(BMP, "BMP (BGP Monitoring Protocol)");
|
|
|
|
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_CONN, "BMP connection state");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_TARGETS, "BMP targets");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_TARGETSNAME, "BMP targets name");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_LISTENER, "BMP listener");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_ACTIVE, "BMP active connection config");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_ACLNAME, "BMP access-list name");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_QUEUE, "BMP update queue item");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP, "BMP instance state");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_MIRRORQ, "BMP route mirroring buffer");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_PEER, "BMP per BGP peer data");
|
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_OPEN, "BMP stored BGP OPEN message");
|
2025-03-12 10:26:12 +01:00
|
|
|
DEFINE_MTYPE_STATIC(BMP, BMP_IMPORTED_BGP, "BMP imported BGP instance");
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
DEFINE_QOBJ_TYPE(bmp_targets);
|
|
|
|
|
|
|
|
static int bmp_bgp_cmp(const struct bmp_bgp *a, const struct bmp_bgp *b)
|
|
|
|
{
|
|
|
|
if (a->bgp < b->bgp)
|
|
|
|
return -1;
|
|
|
|
if (a->bgp > b->bgp)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t bmp_bgp_hash(const struct bmp_bgp *e)
|
|
|
|
{
|
|
|
|
return jhash(&e->bgp, sizeof(e->bgp), 0x55aa5a5a);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_HASH(bmp_bgph, struct bmp_bgp, bbi, bmp_bgp_cmp, bmp_bgp_hash);
|
|
|
|
|
|
|
|
struct bmp_bgph_head bmp_bgph;
|
|
|
|
|
|
|
|
static int bmp_bgp_peer_cmp(const struct bmp_bgp_peer *a,
|
|
|
|
const struct bmp_bgp_peer *b)
|
|
|
|
{
|
|
|
|
if (a->peerid < b->peerid)
|
|
|
|
return -1;
|
|
|
|
if (a->peerid > b->peerid)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t bmp_bgp_peer_hash(const struct bmp_bgp_peer *e)
|
|
|
|
{
|
|
|
|
return e->peerid;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_HASH(bmp_peerh, struct bmp_bgp_peer, bpi,
|
|
|
|
bmp_bgp_peer_cmp, bmp_bgp_peer_hash);
|
|
|
|
|
|
|
|
struct bmp_peerh_head bmp_peerh;
|
|
|
|
|
|
|
|
DECLARE_LIST(bmp_mirrorq, struct bmp_mirrorq, bmi);
|
|
|
|
|
|
|
|
/* listener management */
|
|
|
|
|
|
|
|
static int bmp_listener_cmp(const struct bmp_listener *a,
|
|
|
|
const struct bmp_listener *b)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = sockunion_cmp(&a->addr, &b->addr);
|
|
|
|
if (c)
|
|
|
|
return c;
|
|
|
|
if (a->port < b->port)
|
|
|
|
return -1;
|
|
|
|
if (a->port > b->port)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_UNIQ(bmp_listeners, struct bmp_listener, bli,
|
|
|
|
bmp_listener_cmp);
|
|
|
|
|
|
|
|
static void bmp_listener_put(struct bmp_listener *bl)
|
|
|
|
{
|
|
|
|
bmp_listeners_del(&bl->targets->listeners, bl);
|
|
|
|
XFREE(MTYPE_BMP_LISTENER, bl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_targets_cmp(const struct bmp_targets *a,
|
|
|
|
const struct bmp_targets *b)
|
|
|
|
{
|
|
|
|
return strcmp(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_UNIQ(bmp_targets, struct bmp_targets, bti, bmp_targets_cmp);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_imported_bgps_cmp(const struct bmp_imported_bgp *a, const struct bmp_imported_bgp *b)
|
|
|
|
{
|
|
|
|
if (a->name == NULL && b->name == NULL)
|
|
|
|
return 0;
|
|
|
|
if (a->name == NULL || b->name == NULL)
|
|
|
|
return 1;
|
|
|
|
return strcmp(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_UNIQ(bmp_imported_bgps, struct bmp_imported_bgp, bib, bmp_imported_bgps_cmp);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
DECLARE_LIST(bmp_session, struct bmp, bsi);
|
|
|
|
|
|
|
|
DECLARE_DLIST(bmp_qlist, struct bmp_queue_entry, bli);
|
|
|
|
|
|
|
|
static int bmp_qhash_cmp(const struct bmp_queue_entry *a,
|
|
|
|
const struct bmp_queue_entry *b)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN && b->afi == AFI_L2VPN
|
|
|
|
&& b->safi == SAFI_EVPN) {
|
|
|
|
ret = prefix_cmp(&a->rd, &b->rd);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
} else if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN)
|
|
|
|
return 1;
|
|
|
|
else if (b->afi == AFI_L2VPN && b->safi == SAFI_EVPN)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (a->afi == b->afi && a->safi == SAFI_MPLS_VPN &&
|
|
|
|
b->safi == SAFI_MPLS_VPN) {
|
|
|
|
ret = prefix_cmp(&a->rd, &b->rd);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
} else if (a->safi == SAFI_MPLS_VPN)
|
|
|
|
return 1;
|
|
|
|
else if (b->safi == SAFI_MPLS_VPN)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = prefix_cmp(&a->p, &b->p);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = memcmp(&a->peerid, &b->peerid,
|
|
|
|
offsetof(struct bmp_queue_entry, refcount) -
|
|
|
|
offsetof(struct bmp_queue_entry, peerid));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t bmp_qhash_hkey(const struct bmp_queue_entry *e)
|
|
|
|
{
|
|
|
|
uint32_t key;
|
|
|
|
|
|
|
|
key = prefix_hash_key((void *)&e->p);
|
|
|
|
key = jhash(&e->peerid,
|
|
|
|
offsetof(struct bmp_queue_entry, refcount)
|
|
|
|
- offsetof(struct bmp_queue_entry, peerid),
|
|
|
|
key);
|
|
|
|
if ((e->afi == AFI_L2VPN && e->safi == SAFI_EVPN) ||
|
|
|
|
(e->safi == SAFI_MPLS_VPN))
|
|
|
|
key = jhash(&e->rd,
|
|
|
|
offsetof(struct bmp_queue_entry, rd)
|
|
|
|
- offsetof(struct bmp_queue_entry, refcount)
|
|
|
|
+ PSIZE(e->rd.prefixlen),
|
|
|
|
key);
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_HASH(bmp_qhash, struct bmp_queue_entry, bhi,
|
|
|
|
bmp_qhash_cmp, bmp_qhash_hkey);
|
|
|
|
|
|
|
|
static int bmp_active_cmp(const struct bmp_active *a,
|
|
|
|
const struct bmp_active *b)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = strcmp(a->hostname, b->hostname);
|
|
|
|
if (c)
|
|
|
|
return c;
|
|
|
|
if (a->port < b->port)
|
|
|
|
return -1;
|
|
|
|
if (a->port > b->port)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_SORTLIST_UNIQ(bmp_actives, struct bmp_active, bai, bmp_active_cmp);
|
|
|
|
|
|
|
|
static struct bmp *bmp_new(struct bmp_targets *bt, int bmp_sock)
|
|
|
|
{
|
|
|
|
struct bmp *new = XCALLOC(MTYPE_BMP_CONN, sizeof(struct bmp));
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
monotime(&new->t_up);
|
|
|
|
new->targets = bt;
|
|
|
|
new->socket = bmp_sock;
|
|
|
|
new->syncafi = AFI_MAX;
|
2025-03-12 10:26:12 +01:00
|
|
|
new->sync_bgp = NULL;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
FOREACH_AFI_SAFI (afi, safi) {
|
|
|
|
new->afistate[afi][safi] = bt->afimon[afi][safi]
|
|
|
|
? BMP_AFI_NEEDSYNC : BMP_AFI_INACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bmp_session_add_tail(&bt->sessions, new);
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_free(struct bmp *bmp)
|
|
|
|
{
|
|
|
|
bmp_session_del(&bmp->targets->sessions, 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
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
/* determine the peer type for per-peer headers from a vrf_id
|
|
|
|
* for non loc-rib peer type only
|
|
|
|
*/
|
|
|
|
static inline int bmp_get_peer_type_vrf(vrf_id_t vrf_id)
|
|
|
|
{
|
|
|
|
switch (vrf_id) {
|
|
|
|
case VRF_DEFAULT:
|
|
|
|
return BMP_PEER_TYPE_GLOBAL_INSTANCE;
|
|
|
|
case VRF_UNKNOWN:
|
|
|
|
/* when the VRF is unknown consider it a local instance */
|
|
|
|
return BMP_PEER_TYPE_LOCAL_INSTANCE;
|
|
|
|
default:
|
|
|
|
return BMP_PEER_TYPE_RD_INSTANCE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine the peer type for per-peer headers from a struct peer
|
|
|
|
* provide a bgp->peer_self for loc-rib
|
|
|
|
*/
|
|
|
|
static inline int bmp_get_peer_type(struct peer *peer)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
2025-03-12 10:26:12 +01:00
|
|
|
if (peer->bgp->peer_self == peer)
|
|
|
|
return BMP_PEER_TYPE_LOC_RIB_INSTANCE;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
return bmp_get_peer_type_vrf(peer->bgp->vrf_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int bmp_get_peer_distinguisher(struct bgp *bgp, afi_t afi, uint8_t peer_type,
|
|
|
|
uint64_t *result_ref)
|
|
|
|
{
|
|
|
|
/* use RD if set in VRF config */
|
|
|
|
struct prefix_rd *prd;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* sending vrf_id or rd could be turned into an option at some point */
|
2025-03-12 10:26:12 +01:00
|
|
|
if (peer_type == BMP_PEER_TYPE_LOCAL_INSTANCE || bgp->vrf_id == VRF_UNKNOWN)
|
|
|
|
return 1;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* vrf default => ok, distinguisher 0 */
|
|
|
|
if (bgp->inst_type == VRF_DEFAULT)
|
|
|
|
return (*result_ref = 0);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
prd = &bgp->vpn_policy[AFI_IP].tovpn_rd;
|
|
|
|
if ((afi == AFI_IP || afi == AFI_UNSPEC) &&
|
|
|
|
CHECK_FLAG(bgp->vpn_policy[AFI_IP].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
memcpy(result_ref, prd->val, sizeof(prd->val));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
prd = &bgp->vpn_policy[AFI_IP6].tovpn_rd;
|
|
|
|
if ((afi == AFI_IP6 || afi == AFI_UNSPEC) &&
|
|
|
|
CHECK_FLAG(bgp->vpn_policy[AFI_IP6].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) {
|
|
|
|
memcpy(result_ref, prd->val, sizeof(prd->val));
|
|
|
|
return 0;
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* 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_putc(s, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
#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, peer_type_flag);
|
|
|
|
|
|
|
|
/* Peer Flags */
|
|
|
|
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 */
|
|
|
|
stream_put(s, (uint8_t *)&peer_distinguisher, 8);
|
|
|
|
|
|
|
|
/* Peer Address */
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Peer AS */
|
|
|
|
/* set peer ASN but for LOC-RIB INSTANCE (RFC 9069) put the local bgp
|
|
|
|
* ASN
|
|
|
|
*/
|
|
|
|
as_t asn = !is_locrib ? peer->as : bgp->as;
|
|
|
|
|
|
|
|
stream_putl(s, asn);
|
|
|
|
|
|
|
|
/* Peer BGP ID */
|
|
|
|
/* set router-id but for LOC-RIB INSTANCE (RFC 9069) put the instance
|
|
|
|
* router-id
|
|
|
|
*/
|
|
|
|
struct in_addr *bgp_id =
|
|
|
|
!is_locrib ? &peer->remote_id : &bgp->router_id;
|
|
|
|
|
|
|
|
stream_put_in_addr(s, bgp_id);
|
|
|
|
|
|
|
|
/* Timestamp */
|
|
|
|
if (tv) {
|
|
|
|
stream_putl(s, tv->tv_sec);
|
|
|
|
stream_putl(s, tv->tv_usec);
|
|
|
|
} else {
|
|
|
|
stream_putl(s, 0);
|
|
|
|
stream_putl(s, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_put_info_tlv(struct stream *s, uint16_t type,
|
|
|
|
const char *string)
|
|
|
|
{
|
|
|
|
int len = strlen (string);
|
|
|
|
stream_putw(s, type);
|
|
|
|
stream_putw(s, len);
|
|
|
|
stream_put(s, string, len);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
/* put the vrf table name of the bgp instance bmp is bound to in a tlv on the
|
|
|
|
* stream
|
|
|
|
*/
|
|
|
|
static void bmp_put_vrftablename_info_tlv(struct stream *s, struct peer *peer)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
2025-03-12 10:26:12 +01:00
|
|
|
const char *vrftablename = "global";
|
|
|
|
struct vrf *vrf;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
#define BMP_INFO_TYPE_VRFTABLENAME 3
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if (peer->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
|
|
|
|
vrf = vrf_lookup_by_id(peer->bgp->vrf_id);
|
2025-02-05 10:03:58 +01:00
|
|
|
vrftablename = vrf ? vrf->name : NULL;
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
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 = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION);
|
|
|
|
|
|
|
|
#define BMP_INIT_INFO_TYPE_SYSDESCR 1
|
|
|
|
#define BMP_INIT_INFO_TYPE_SYSNAME 2
|
|
|
|
bmp_put_info_tlv(s, BMP_INIT_INFO_TYPE_SYSDESCR,
|
|
|
|
FRR_FULL_NAME " " FRR_VER_SHORT);
|
|
|
|
bmp_put_info_tlv(s, BMP_INIT_INFO_TYPE_SYSNAME, cmd_hostname_get());
|
|
|
|
|
|
|
|
len = stream_get_endp(s);
|
|
|
|
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
|
|
|
|
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
stream_free(s);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy)
|
|
|
|
{
|
|
|
|
size_t len_pos;
|
|
|
|
uint8_t marker[16] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff,
|
|
|
|
};
|
|
|
|
|
|
|
|
stream_put(s, marker, sizeof(marker));
|
|
|
|
len_pos = stream_get_endp(s);
|
|
|
|
stream_putw(s, 0);
|
|
|
|
stream_putc(s, BGP_MSG_NOTIFY);
|
|
|
|
stream_putc(s, nfy->code);
|
|
|
|
stream_putc(s, nfy->subcode);
|
|
|
|
stream_put(s, nfy->data, nfy->length);
|
|
|
|
|
|
|
|
stream_putw_at(s, len_pos, stream_get_endp(s) - len_pos
|
|
|
|
+ sizeof(marker));
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
/* send peer up/down for peer based on down boolean value
|
|
|
|
* returns the message to send or NULL if the peer_distinguisher is not
|
|
|
|
* available
|
|
|
|
*/
|
2025-02-05 10:03:58 +01:00
|
|
|
static struct stream *bmp_peerstate(struct peer *peer, bool down)
|
|
|
|
{
|
|
|
|
struct stream *s;
|
|
|
|
size_t len;
|
|
|
|
struct timeval uptime, uptime_real;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type;
|
|
|
|
bool is_locrib = false;
|
|
|
|
uint64_t peer_distinguisher = 0;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
uptime.tv_sec = peer->uptime;
|
|
|
|
uptime.tv_usec = 0;
|
|
|
|
monotime_to_realtime(&uptime, &uptime_real);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
peer_type = bmp_get_peer_type(peer);
|
|
|
|
if (peer_type == BMP_PEER_TYPE_LOC_RIB_INSTANCE)
|
|
|
|
is_locrib = true;
|
|
|
|
|
|
|
|
if (bmp_get_peer_distinguisher(peer->bgp, AFI_UNSPEC, peer_type, &peer_distinguisher)) {
|
|
|
|
zlog_warn("skipping bmp message for peer %s: can't get peer distinguisher",
|
|
|
|
peer->host);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
#define BGP_BMP_MAX_PACKET_SIZE 1024
|
|
|
|
#define BMP_PEERUP_INFO_TYPE_STRING 0
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if ((peer_established(peer->connection) || is_locrib) && !down) {
|
2025-02-05 10:03:58 +01:00
|
|
|
struct bmp_bgp_peer *bbpeer;
|
|
|
|
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3,
|
|
|
|
BMP_TYPE_PEER_UP_NOTIFICATION);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(s, peer->bgp, peer, 0, peer_type, peer_distinguisher, &uptime_real);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* Local Address (16 bytes) */
|
2025-03-12 10:26:12 +01:00
|
|
|
if (is_locrib)
|
|
|
|
stream_put(s, 0, 16);
|
|
|
|
else if (peer->connection->su_local->sa.sa_family == AF_INET6)
|
|
|
|
stream_put(s, &peer->connection->su_local->sin6.sin6_addr, 16);
|
|
|
|
else if (peer->connection->su_local->sa.sa_family == AF_INET) {
|
2025-02-05 10:03:58 +01:00
|
|
|
stream_putl(s, 0);
|
|
|
|
stream_putl(s, 0);
|
|
|
|
stream_putl(s, 0);
|
2025-03-12 10:26:12 +01:00
|
|
|
stream_put_in_addr(s, &peer->connection->su_local->sin.sin_addr);
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Local Port, Remote Port */
|
2025-03-12 10:26:12 +01:00
|
|
|
if (!peer->connection->su_local || is_locrib)
|
|
|
|
stream_putw(s, 0);
|
|
|
|
else if (peer->connection->su_local->sa.sa_family == AF_INET6)
|
|
|
|
stream_putw(s, htons(peer->connection->su_local->sin6.sin6_port));
|
|
|
|
else if (peer->connection->su_local->sa.sa_family == AF_INET)
|
|
|
|
stream_putw(s, htons(peer->connection->su_local->sin.sin_port));
|
|
|
|
|
|
|
|
if (!peer->connection->su_remote || is_locrib)
|
|
|
|
stream_putw(s, 0);
|
|
|
|
else if (peer->connection->su_remote->sa.sa_family == AF_INET6)
|
|
|
|
stream_putw(s, htons(peer->connection->su_remote->sin6.sin6_port));
|
|
|
|
else if (peer->connection->su_remote->sa.sa_family == AF_INET)
|
|
|
|
stream_putw(s, htons(peer->connection->su_remote->sin.sin_port));
|
|
|
|
|
|
|
|
/* TODO craft message with fields & capabilities for loc-rib */
|
2025-02-05 10:03:58 +01:00
|
|
|
static const uint8_t dummy_open[] = {
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
0x00, 0x13, 0x01,
|
|
|
|
};
|
|
|
|
|
|
|
|
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
|
|
|
|
|
|
|
|
if (bbpeer && bbpeer->open_tx)
|
|
|
|
stream_put(s, bbpeer->open_tx, bbpeer->open_tx_len);
|
|
|
|
else {
|
|
|
|
stream_put(s, dummy_open, sizeof(dummy_open));
|
|
|
|
zlog_warn("bmp: missing TX OPEN message for peer %s",
|
|
|
|
peer->host);
|
|
|
|
}
|
|
|
|
if (bbpeer && bbpeer->open_rx)
|
|
|
|
stream_put(s, bbpeer->open_rx, bbpeer->open_rx_len);
|
|
|
|
else {
|
|
|
|
stream_put(s, dummy_open, sizeof(dummy_open));
|
|
|
|
zlog_warn("bmp: missing RX OPEN message for peer %s",
|
|
|
|
peer->host);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peer->desc)
|
|
|
|
bmp_put_info_tlv(s, BMP_PEERUP_INFO_TYPE_STRING,
|
|
|
|
peer->desc);
|
|
|
|
} else {
|
|
|
|
uint8_t type;
|
|
|
|
size_t type_pos;
|
|
|
|
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3,
|
|
|
|
BMP_TYPE_PEER_DOWN_NOTIFICATION);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(s, peer->bgp, peer, 0, peer_type, peer_distinguisher, &uptime_real);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
type_pos = stream_get_endp(s);
|
|
|
|
stream_putc(s, 0); /* placeholder for down reason */
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if (is_locrib) {
|
|
|
|
type = BMP_PEERDOWN_LOCAL_TLV;
|
|
|
|
} else {
|
|
|
|
switch (peer->last_reset) {
|
|
|
|
case PEER_DOWN_NOTIFY_RECEIVED:
|
|
|
|
type = BMP_PEERDOWN_REMOTE_NOTIFY;
|
|
|
|
bmp_notify_put(s, &peer->notify);
|
|
|
|
break;
|
|
|
|
case PEER_DOWN_CLOSE_SESSION:
|
|
|
|
type = BMP_PEERDOWN_REMOTE_CLOSE;
|
|
|
|
break;
|
|
|
|
case PEER_DOWN_WAITING_NHT:
|
|
|
|
type = BMP_PEERDOWN_LOCAL_FSM;
|
|
|
|
stream_putw(s, BGP_FSM_TcpConnectionFails);
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* TODO: Map remaining PEER_DOWN_* reasons to RFC event
|
|
|
|
* codes.
|
|
|
|
* TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY.
|
|
|
|
*
|
|
|
|
* See RFC7854 ss. 4.9
|
|
|
|
*/
|
|
|
|
default:
|
|
|
|
type = BMP_PEERDOWN_LOCAL_FSM;
|
|
|
|
stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE);
|
|
|
|
break;
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
stream_putc_at(s, type_pos, type);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if (is_locrib)
|
|
|
|
bmp_put_vrftablename_info_tlv(s, peer);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
len = stream_get_endp(s);
|
|
|
|
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_send_peerup_per_instance(struct bmp *bmp, struct bgp *bgp)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
|
|
|
struct peer *peer;
|
|
|
|
struct listnode *node;
|
|
|
|
struct stream *s;
|
|
|
|
|
|
|
|
/* Walk down all peers */
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
s = bmp_peerstate(peer, false);
|
2025-03-12 10:26:12 +01:00
|
|
|
if (s) {
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_send_peerup(struct bmp *bmp)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bgp *bgp;
|
|
|
|
|
|
|
|
bmp_send_peerup_per_instance(bmp, bmp->targets->bgp);
|
|
|
|
frr_each (bmp_imported_bgps, &bmp->targets->imported_bgps, bib) {
|
|
|
|
bgp = bgp_lookup_by_name(bib->name);
|
|
|
|
if (bgp)
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_send_peerup_vrf_per_instance(struct bmp *bmp, enum bmp_vrf_state *vrf_state,
|
|
|
|
struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct stream *s;
|
|
|
|
|
|
|
|
/* send unconditionally because state may has been set before the
|
|
|
|
* session was up. and in this case the peer up has not been sent.
|
|
|
|
*/
|
|
|
|
bmp_bgp_update_vrf_status(vrf_state, bgp, vrf_state_unknown);
|
|
|
|
|
|
|
|
s = bmp_peerstate(bgp->peer_self, *vrf_state == vrf_state_down);
|
|
|
|
if (s) {
|
2025-02-05 10:03:58 +01:00
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
stream_free(s);
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_send_peerup_vrf(struct bmp *bmp)
|
|
|
|
{
|
|
|
|
struct bgp *bgp;
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bmp_bgp *bmpbgp = bmp->targets->bmpbgp;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
|
|
|
|
bmp_send_peerup_vrf_per_instance(bmp, &bmpbgp->vrf_state, bmpbgp->bgp);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib) {
|
|
|
|
bgp = bgp_lookup_by_name(bib->name);
|
|
|
|
if (!bgp)
|
|
|
|
continue;
|
|
|
|
bmp_send_peerup_vrf_per_instance(bmp, &bib->vrf_state, bgp);
|
|
|
|
}
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static void bmp_send_bt(struct bmp_targets *bt, struct stream *s)
|
|
|
|
{
|
|
|
|
struct bmp *bmp;
|
|
|
|
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp)
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_send_bt_safe(struct bmp_targets *bt, struct stream *s)
|
|
|
|
{
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_send_peerdown_vrf_per_instance(struct bmp_targets *bt, struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct stream *s;
|
|
|
|
|
|
|
|
s = bmp_peerstate(bgp->peer_self, true);
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send a stream to all bmp sessions configured in a bgp instance */
|
2025-02-05 10:03:58 +01:00
|
|
|
/* XXX: kludge - filling the pullwr's buffer */
|
|
|
|
static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
|
|
|
|
{
|
|
|
|
struct bmp_targets *bt;
|
2025-03-12 10:26:12 +01:00
|
|
|
|
|
|
|
if (!s)
|
|
|
|
return;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
frr_each(bmp_targets, &bmpbgp->targets, bt)
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static void bmp_send_all_safe(struct bmp_bgp *bmpbgp, struct stream *s)
|
|
|
|
{
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!bmpbgp) {
|
|
|
|
stream_free(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bmp_send_all(bmpbgp, s);
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
/*
|
|
|
|
* Route Mirroring
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define BMP_MIRROR_TLV_TYPE_BGP_MESSAGE 0
|
|
|
|
#define BMP_MIRROR_TLV_TYPE_INFO 1
|
|
|
|
|
|
|
|
#define BMP_MIRROR_INFO_CODE_ERRORPDU 0
|
|
|
|
#define BMP_MIRROR_INFO_CODE_LOSTMSGS 1
|
|
|
|
|
|
|
|
static struct bmp_mirrorq *bmp_pull_mirror(struct bmp *bmp)
|
|
|
|
{
|
|
|
|
struct bmp_mirrorq *bmq;
|
|
|
|
|
|
|
|
bmq = bmp->mirrorpos;
|
|
|
|
if (!bmq)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
bmp->mirrorpos = bmp_mirrorq_next(&bmp->targets->bmpbgp->mirrorq, bmq);
|
|
|
|
|
|
|
|
bmq->refcount--;
|
|
|
|
if (!bmq->refcount) {
|
|
|
|
bmp->targets->bmpbgp->mirror_qsize -= sizeof(*bmq) + bmq->len;
|
|
|
|
bmp_mirrorq_del(&bmp->targets->bmpbgp->mirrorq, bmq);
|
|
|
|
}
|
|
|
|
return bmq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_mirror_cull(struct bmp_bgp *bmpbgp)
|
|
|
|
{
|
|
|
|
while (bmpbgp->mirror_qsize > bmpbgp->mirror_qsizelimit) {
|
|
|
|
struct bmp_mirrorq *bmq, *inner;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp *bmp;
|
|
|
|
|
|
|
|
bmq = bmp_mirrorq_first(&bmpbgp->mirrorq);
|
|
|
|
|
|
|
|
frr_each(bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
if (!bt->mirror)
|
|
|
|
continue;
|
|
|
|
frr_each(bmp_session, &bt->sessions, bmp) {
|
|
|
|
if (bmp->mirrorpos != bmq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while ((inner = bmp_pull_mirror(bmp))) {
|
|
|
|
if (!inner->refcount)
|
|
|
|
XFREE(MTYPE_BMP_MIRRORQ,
|
|
|
|
inner);
|
|
|
|
}
|
|
|
|
|
|
|
|
zlog_warn("bmp[%s] lost mirror messages due to buffer size limit",
|
|
|
|
bmp->remote);
|
|
|
|
bmp->mirror_lost = true;
|
|
|
|
pullwr_bump(bmp->pullwr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_mirror_packet(struct peer *peer, uint8_t type, bgp_size_t size,
|
|
|
|
struct stream *packet)
|
|
|
|
{
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_bgp *bmpbgp;
|
2025-02-05 10:03:58 +01:00
|
|
|
struct timeval tv;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_mirrorq *qitem = NULL;
|
2025-02-05 10:03:58 +01:00
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp *bmp;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct listnode *node;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
frrtrace(3, frr_bgp, bmp_mirror_packet, peer, type, packet);
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
if (type == BGP_MSG_OPEN) {
|
|
|
|
struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer);
|
|
|
|
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
|
|
|
|
bbpeer->open_rx_len = size;
|
|
|
|
bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, size);
|
|
|
|
memcpy(bbpeer->open_rx, packet->data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qitem = XCALLOC(MTYPE_BMP_MIRRORQ, sizeof(*qitem) + size);
|
|
|
|
qitem->peerid = peer->qobj_node.nid;
|
|
|
|
qitem->tv = tv;
|
|
|
|
qitem->len = size;
|
|
|
|
memcpy(qitem->data, packet->data, size);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
2025-02-05 10:03:58 +01:00
|
|
|
continue;
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
if (!bt->mirror)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (bgp_vrf != peer->bgp && !bmp_imported_bgp_find(bt, peer->bgp->name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
if (!qitem) {
|
|
|
|
qitem = XCALLOC(MTYPE_BMP_MIRRORQ, sizeof(*qitem) + size);
|
|
|
|
qitem->peerid = peer->qobj_node.nid;
|
|
|
|
qitem->tv = tv;
|
|
|
|
qitem->len = size;
|
|
|
|
memcpy(qitem->data, packet->data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
qitem->refcount++;
|
|
|
|
if (!bmp->mirrorpos)
|
|
|
|
bmp->mirrorpos = qitem;
|
|
|
|
pullwr_bump(bmp->pullwr);
|
|
|
|
}
|
|
|
|
bmpbgp->mirror_qsize += sizeof(*qitem) + size;
|
|
|
|
bmp_mirrorq_add_tail(&bmpbgp->mirrorq, qitem);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_mirror_cull(bmpbgp);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmpbgp->mirror_qsizemax = MAX(bmpbgp->mirror_qsizemax, bmpbgp->mirror_qsize);
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
if (qitem && qitem->refcount == 0)
|
|
|
|
XFREE(MTYPE_BMP_MIRRORQ, qitem);
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr)
|
|
|
|
{
|
|
|
|
struct stream *s;
|
|
|
|
struct timeval tv;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type_flag;
|
|
|
|
uint64_t peer_distinguisher = 0;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
peer_type_flag = bmp_get_peer_type_vrf(bmp->targets->bgp->vrf_id);
|
|
|
|
|
|
|
|
if (bmp_get_peer_distinguisher(bmp->targets->bgp, AFI_UNSPEC, peer_type_flag,
|
|
|
|
&peer_distinguisher)) {
|
|
|
|
zlog_warn("skipping bmp message for reason: can't get peer distinguisher");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(s, bmp->targets->bgp, bmp->targets->bgp->peer_self, 0, peer_type_flag,
|
|
|
|
peer_distinguisher, &tv);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO);
|
|
|
|
stream_putw(s, 2);
|
|
|
|
stream_putw(s, BMP_MIRROR_INFO_CODE_LOSTMSGS);
|
|
|
|
stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s));
|
|
|
|
|
|
|
|
bmp->cnt_mirror_overruns++;
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr)
|
|
|
|
{
|
|
|
|
struct bmp_mirrorq *bmq;
|
|
|
|
struct peer *peer;
|
|
|
|
bool written = false;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type_flag;
|
|
|
|
uint64_t peer_distinguisher = 0;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (bmp->mirror_lost) {
|
|
|
|
bmp_wrmirror_lost(bmp, pullwr);
|
|
|
|
bmp->mirror_lost = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bmq = bmp_pull_mirror(bmp);
|
|
|
|
if (!bmq)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
peer = QOBJ_GET_TYPESAFE(bmq->peerid, peer);
|
|
|
|
if (!peer) {
|
|
|
|
zlog_info("bmp: skipping mirror message for deleted peer");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
peer_type_flag = bmp_get_peer_type_vrf(bmp->targets->bgp->vrf_id);
|
|
|
|
|
|
|
|
if (bmp_get_peer_distinguisher(peer->bgp, AFI_UNSPEC, peer_type_flag, &peer_distinguisher)) {
|
|
|
|
zlog_warn("skipping bmp message for peer %s: can't get peer distinguisher",
|
|
|
|
peer->host);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
struct stream *s;
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(s, peer->bgp, peer, 0, peer_type_flag, peer_distinguisher, &bmq->tv);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* BMP Mirror TLV. */
|
|
|
|
stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE);
|
|
|
|
stream_putw(s, bmq->len);
|
|
|
|
stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s) + bmq->len);
|
|
|
|
|
|
|
|
bmp->cnt_mirror++;
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
pullwr_write(bmp->pullwr, bmq->data, bmq->len);
|
|
|
|
|
|
|
|
stream_free(s);
|
|
|
|
written = true;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (!bmq->refcount)
|
|
|
|
XFREE(MTYPE_BMP_MIRRORQ, bmq);
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_outgoing_packet(struct peer *peer, uint8_t type, bgp_size_t size,
|
|
|
|
struct stream *packet)
|
|
|
|
{
|
|
|
|
if (type == BGP_MSG_OPEN) {
|
|
|
|
frrtrace(2, frr_bgp, bmp_update_saved_open, peer, packet);
|
|
|
|
|
|
|
|
struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer);
|
|
|
|
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
|
|
|
|
bbpeer->open_tx_len = size;
|
|
|
|
bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, size);
|
|
|
|
memcpy(bbpeer->open_tx, packet->data, size);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_peer_status_changed(struct peer *peer)
|
|
|
|
{
|
|
|
|
struct bmp_bgp_peer *bbpeer, *bbdopp;
|
|
|
|
|
|
|
|
frrtrace(1, frr_bgp, bmp_peer_status_changed, peer);
|
|
|
|
|
|
|
|
if (peer->connection->status == Deleted) {
|
|
|
|
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
|
|
|
|
if (bbpeer) {
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
bmp_peerh_del(&bmp_peerh, bbpeer);
|
|
|
|
XFREE(MTYPE_BMP_PEER, bbpeer);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this peer just went to Established */
|
|
|
|
if ((peer->connection->ostatus != OpenConfirm) ||
|
|
|
|
!(peer_established(peer->connection)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (peer->doppelganger &&
|
|
|
|
(peer->doppelganger->connection->status != Deleted)) {
|
|
|
|
bbpeer = bmp_bgp_peer_get(peer);
|
|
|
|
bbdopp = bmp_bgp_peer_find(peer->doppelganger->qobj_node.nid);
|
|
|
|
if (bbdopp) {
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
|
|
|
|
bbpeer->open_tx = bbdopp->open_tx;
|
|
|
|
bbpeer->open_tx_len = bbdopp->open_tx_len;
|
|
|
|
bbpeer->open_rx = bbdopp->open_rx;
|
|
|
|
bbpeer->open_rx_len = bbdopp->open_rx_len;
|
|
|
|
|
|
|
|
bmp_peerh_del(&bmp_peerh, bbdopp);
|
|
|
|
XFREE(MTYPE_BMP_PEER, bbdopp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_send_all_bgp(peer, false);
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_peer_backward(struct peer *peer)
|
|
|
|
{
|
|
|
|
struct bmp_bgp_peer *bbpeer;
|
|
|
|
|
|
|
|
frrtrace(1, frr_bgp, bmp_peer_backward_transition, peer);
|
|
|
|
|
|
|
|
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
|
|
|
|
if (bbpeer) {
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
bbpeer->open_tx_len = 0;
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
bbpeer->open_rx_len = 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_send_all_bgp(peer, true);
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags, uint8_t peer_type_flag,
|
|
|
|
struct bgp *bgp)
|
2025-02-05 10:03:58 +01:00
|
|
|
{
|
|
|
|
struct peer *peer;
|
|
|
|
struct listnode *node;
|
|
|
|
struct stream *s, *s2;
|
|
|
|
iana_afi_t pkt_afi = IANA_AFI_IPV4;
|
|
|
|
iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frrtrace(5, frr_bgp, bmp_eor, afi, safi, flags, peer_type_flag, bgp);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
/* Make BGP update packet. */
|
|
|
|
bgp_packet_set_marker(s, BGP_MSG_UPDATE);
|
|
|
|
|
|
|
|
/* Unfeasible Routes Length */
|
|
|
|
stream_putw(s, 0);
|
|
|
|
|
|
|
|
if (afi == AFI_IP && safi == SAFI_UNICAST) {
|
|
|
|
/* Total Path Attribute Length */
|
|
|
|
stream_putw(s, 0);
|
|
|
|
} else {
|
|
|
|
/* Convert AFI, SAFI to values for packet. */
|
|
|
|
bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
|
|
|
|
|
|
|
|
/* Total Path Attribute Length */
|
|
|
|
stream_putw(s, 6);
|
|
|
|
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
|
|
|
|
stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI);
|
|
|
|
stream_putc(s, 3);
|
|
|
|
stream_putw(s, pkt_afi);
|
|
|
|
stream_putc(s, pkt_safi);
|
|
|
|
}
|
|
|
|
|
|
|
|
bgp_packet_set_size(s);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
if (!peer->afc_nego[afi][safi])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
uint64_t peer_distinguisher = 0;
|
|
|
|
/* skip this message if peer distinguisher is not available */
|
2025-03-12 10:26:12 +01:00
|
|
|
if (bmp_get_peer_distinguisher(peer->bgp, afi, peer_type_flag, &peer_distinguisher)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
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);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(s2, bgp, peer, flags, peer_type_flag, peer_distinguisher, NULL);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
stream_putl_at(s2, BMP_LENGTH_POS,
|
|
|
|
stream_get_endp(s) + stream_get_endp(s2));
|
|
|
|
|
|
|
|
bmp->cnt_update++;
|
|
|
|
pullwr_write_stream(bmp->pullwr, s2);
|
|
|
|
pullwr_write_stream(bmp->pullwr, s);
|
|
|
|
stream_free(s2);
|
|
|
|
}
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd,
|
|
|
|
struct peer *peer, struct attr *attr,
|
|
|
|
afi_t afi, safi_t safi, mpls_label_t *label,
|
|
|
|
uint32_t num_labels)
|
|
|
|
{
|
|
|
|
struct bpacket_attr_vec_arr vecarr;
|
|
|
|
struct stream *s;
|
|
|
|
size_t attrlen_pos = 0, mpattrlen_pos = 0;
|
|
|
|
bgp_size_t total_attr_len = 0;
|
|
|
|
|
|
|
|
bpacket_attr_vec_arr_reset(&vecarr);
|
|
|
|
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
bgp_packet_set_marker(s, BGP_MSG_UPDATE);
|
|
|
|
|
|
|
|
/* 2: withdrawn routes length */
|
|
|
|
stream_putw(s, 0);
|
|
|
|
|
|
|
|
/* 3: total attributes length - attrlen_pos stores the position */
|
|
|
|
attrlen_pos = stream_get_endp(s);
|
|
|
|
stream_putw(s, 0);
|
|
|
|
|
|
|
|
/* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
|
2025-02-05 10:17:11 +01:00
|
|
|
total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, &vecarr, NULL, afi, safi, peer,
|
|
|
|
NULL, NULL, 0, 0, 0);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* space check? */
|
|
|
|
|
|
|
|
/* peer_cap_enhe & add-path removed */
|
|
|
|
if (afi == AFI_IP && safi == SAFI_UNICAST)
|
|
|
|
stream_put_prefix(s, p);
|
|
|
|
else {
|
|
|
|
size_t p1 = stream_get_endp(s);
|
|
|
|
|
|
|
|
/* MPLS removed for now */
|
|
|
|
|
|
|
|
mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
|
|
|
|
&vecarr, attr);
|
|
|
|
bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
|
|
|
|
num_labels, 0, 0, attr);
|
|
|
|
bgp_packet_mpattr_end(s, mpattrlen_pos);
|
|
|
|
total_attr_len += stream_get_endp(s) - p1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the total attribute length correctly */
|
|
|
|
stream_putw_at(s, attrlen_pos, total_attr_len);
|
|
|
|
bgp_packet_set_size(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct stream *bmp_withdraw(const struct prefix *p,
|
|
|
|
struct prefix_rd *prd, afi_t afi,
|
|
|
|
safi_t safi)
|
|
|
|
{
|
|
|
|
struct stream *s;
|
|
|
|
size_t attrlen_pos = 0, mp_start, mplen_pos;
|
|
|
|
bgp_size_t total_attr_len = 0;
|
|
|
|
bgp_size_t unfeasible_len;
|
|
|
|
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
|
|
|
|
bgp_packet_set_marker(s, BGP_MSG_UPDATE);
|
|
|
|
stream_putw(s, 0);
|
|
|
|
|
|
|
|
if (afi == AFI_IP && safi == SAFI_UNICAST) {
|
|
|
|
stream_put_prefix(s, p);
|
|
|
|
unfeasible_len = stream_get_endp(s) - BGP_HEADER_SIZE
|
|
|
|
- BGP_UNFEASIBLE_LEN;
|
|
|
|
stream_putw_at(s, BGP_HEADER_SIZE, unfeasible_len);
|
|
|
|
stream_putw(s, 0);
|
|
|
|
} else {
|
|
|
|
attrlen_pos = stream_get_endp(s);
|
|
|
|
/* total attr length = 0 for now. reevaluate later */
|
|
|
|
stream_putw(s, 0);
|
|
|
|
mp_start = stream_get_endp(s);
|
|
|
|
mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
|
|
|
|
|
|
|
|
bgp_packet_mpunreach_prefix(s, p, afi, safi, prd, NULL, 0, 0, 0,
|
|
|
|
NULL);
|
|
|
|
/* Set the mp_unreach attr's length */
|
|
|
|
bgp_packet_mpunreach_end(s, mplen_pos);
|
|
|
|
|
|
|
|
/* Set total path attribute length. */
|
|
|
|
total_attr_len = stream_get_endp(s) - mp_start;
|
|
|
|
stream_putw_at(s, attrlen_pos, total_attr_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
bgp_packet_set_size(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
|
|
|
|
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, mpls_label_t *label,
|
|
|
|
uint32_t num_labels)
|
|
|
|
{
|
|
|
|
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 */
|
2025-03-12 10:26:12 +01:00
|
|
|
if (bmp_get_peer_distinguisher(peer->bgp, afi, peer_type_flag, &peer_distinguisher)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
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, label,
|
|
|
|
num_labels);
|
|
|
|
else
|
|
|
|
msg = bmp_withdraw(p, prd, afi, safi);
|
|
|
|
|
|
|
|
hdr = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_per_peer_hdr(hdr, peer->bgp, peer, flags, peer_type_flag, peer_distinguisher,
|
2025-02-05 10:03:58 +01:00
|
|
|
uptime == (time_t)(-1L) ? NULL : &uptime_real);
|
|
|
|
|
|
|
|
stream_putl_at(hdr, BMP_LENGTH_POS,
|
|
|
|
stream_get_endp(hdr) + stream_get_endp(msg));
|
|
|
|
|
|
|
|
bmp->cnt_update++;
|
|
|
|
pullwr_write_stream(bmp->pullwr, hdr);
|
|
|
|
pullwr_write_stream(bmp->pullwr, msg);
|
|
|
|
stream_free(hdr);
|
|
|
|
stream_free(msg);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static struct bgp *bmp_get_next_bgp(struct bmp_targets *bt, struct bgp *bgp, afi_t afi, safi_t safi)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bgp *bgp_inst;
|
|
|
|
bool get_first = false;
|
|
|
|
|
|
|
|
if (bgp == NULL && bt->bgp_request_sync[afi][safi])
|
|
|
|
return bt->bgp;
|
|
|
|
if (bgp == NULL)
|
|
|
|
get_first = true;
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib) {
|
|
|
|
bgp_inst = bgp_lookup_by_name(bib->name);
|
|
|
|
if (get_first && bgp_inst && bib->bgp_request_sync[afi][safi])
|
|
|
|
return bgp_inst;
|
|
|
|
if (bgp_inst == bgp)
|
|
|
|
get_first = true;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_update_syncro(struct bmp *bmp, afi_t afi, safi_t safi, struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
|
|
|
|
if (bmp->syncafi == afi && bmp->syncsafi == safi) {
|
|
|
|
bmp->syncafi = AFI_MAX;
|
|
|
|
bmp->syncsafi = SAFI_MAX;
|
|
|
|
bmp->sync_bgp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bmp->targets->afimon[afi][safi]) {
|
|
|
|
bmp->afistate[afi][safi] = BMP_AFI_INACTIVE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bmp->afistate[afi][safi] = BMP_AFI_NEEDSYNC;
|
|
|
|
|
|
|
|
if (bgp == NULL || bmp->targets->bgp == bgp)
|
|
|
|
bmp->targets->bgp_request_sync[afi][safi] = true;
|
|
|
|
|
|
|
|
frr_each (bmp_imported_bgps, &bmp->targets->imported_bgps, bib) {
|
|
|
|
if (bgp != NULL && bgp_lookup_by_name(bib->name) != bgp)
|
|
|
|
continue;
|
|
|
|
bib->bgp_request_sync[afi][safi] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_update_syncro_set(struct bmp *bmp, afi_t afi, safi_t safi, struct bgp *bgp,
|
|
|
|
enum bmp_afi_state state)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
|
|
|
|
bmp->afistate[afi][safi] = state;
|
|
|
|
bmp->syncafi = AFI_MAX;
|
|
|
|
bmp->syncsafi = SAFI_MAX;
|
|
|
|
if (bgp == NULL || bmp->targets->bgp == bmp->sync_bgp)
|
|
|
|
bmp->targets->bgp_request_sync[afi][safi] = false;
|
|
|
|
|
|
|
|
frr_each (bmp_imported_bgps, &bmp->targets->imported_bgps, bib) {
|
|
|
|
if (bgp == NULL || bgp_lookup_by_name(bib->name) != bmp->sync_bgp)
|
|
|
|
continue;
|
|
|
|
bib->bgp_request_sync[afi][safi] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_eor_afi_safi(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t peer_type_flag)
|
|
|
|
{
|
|
|
|
struct bgp *sync_bgp;
|
|
|
|
|
|
|
|
zlog_info("bmp[%s] %s %s table completed (EoR) (BGP %s)", bmp->remote, afi2str(afi),
|
|
|
|
safi2str(safi), bmp->sync_bgp->name_pretty);
|
|
|
|
|
|
|
|
bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L, peer_type_flag, bmp->sync_bgp);
|
|
|
|
bmp_eor(bmp, afi, safi, 0, peer_type_flag, bmp->sync_bgp);
|
|
|
|
bmp_eor(bmp, afi, safi, 0, BMP_PEER_TYPE_LOC_RIB_INSTANCE, bmp->sync_bgp);
|
|
|
|
|
|
|
|
sync_bgp = bmp_get_next_bgp(bmp->targets, bmp->sync_bgp, afi, safi);
|
|
|
|
if (sync_bgp) {
|
|
|
|
memset(&bmp->syncpos, 0, sizeof(bmp->syncpos));
|
|
|
|
bmp->syncpos.family = afi2family(afi);
|
|
|
|
bmp->syncrdpos = NULL;
|
|
|
|
bmp->syncpeerid = 0;
|
|
|
|
} else
|
|
|
|
bmp_update_syncro_set(bmp, afi, safi, bmp->sync_bgp, BMP_AFI_LIVE);
|
|
|
|
bmp->sync_bgp = sync_bgp;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static bool bmp_wrsync(struct bmp *bmp, struct pullwr *pullwr)
|
|
|
|
{
|
2025-02-05 10:17:11 +01:00
|
|
|
uint8_t bpi_num_labels, adjin_num_labels;
|
2025-02-05 10:03:58 +01:00
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type_flag;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (bmp->syncafi == AFI_MAX) {
|
|
|
|
FOREACH_AFI_SAFI (afi, safi) {
|
|
|
|
if (bmp->afistate[afi][safi] != BMP_AFI_NEEDSYNC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bmp->afistate[afi][safi] = BMP_AFI_SYNC;
|
|
|
|
|
|
|
|
bmp->syncafi = afi;
|
|
|
|
bmp->syncsafi = safi;
|
|
|
|
bmp->syncpeerid = 0;
|
|
|
|
memset(&bmp->syncpos, 0, sizeof(bmp->syncpos));
|
|
|
|
bmp->syncpos.family = afi2family(afi);
|
|
|
|
bmp->syncrdpos = NULL;
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp->sync_bgp = bmp_get_next_bgp(bmp->targets, NULL, afi, safi);
|
|
|
|
if (bmp->sync_bgp == NULL)
|
|
|
|
/* all BGP instances already synced*/
|
|
|
|
return true;
|
|
|
|
zlog_info("bmp[%s] %s %s sending table (BGP %s)", bmp->remote,
|
|
|
|
afi2str(bmp->syncafi), safi2str(bmp->syncsafi),
|
|
|
|
bmp->sync_bgp->name_pretty);
|
2025-02-05 10:03:58 +01:00
|
|
|
/* break does not work here, 2 loops... */
|
|
|
|
goto afibreak;
|
|
|
|
}
|
|
|
|
if (bmp->syncafi == AFI_MAX)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
afibreak:
|
|
|
|
afi = bmp->syncafi;
|
|
|
|
safi = bmp->syncsafi;
|
|
|
|
|
|
|
|
if (!bmp->targets->afimon[afi][safi]) {
|
|
|
|
/* shouldn't happen */
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_update_syncro_set(bmp, afi, safi, bmp->sync_bgp, BMP_AFI_INACTIVE);
|
|
|
|
bmp->sync_bgp = NULL;
|
2025-02-05 10:03:58 +01:00
|
|
|
return true;
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
if (bmp->sync_bgp == NULL) {
|
|
|
|
bmp->sync_bgp = bmp_get_next_bgp(bmp->targets, NULL, afi, safi);
|
|
|
|
if (bmp->sync_bgp == NULL)
|
|
|
|
return true;
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bgp_table *table = bmp->sync_bgp->rib[afi][safi];
|
2025-02-05 10:03:58 +01:00
|
|
|
struct bgp_dest *bn = NULL;
|
|
|
|
struct bgp_path_info *bpi = NULL, *bpiter;
|
|
|
|
struct bgp_adj_in *adjin = NULL, *adjiter;
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
peer_type_flag = bmp_get_peer_type_vrf(bmp->sync_bgp->vrf_id);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
if ((afi == AFI_L2VPN && safi == SAFI_EVPN) ||
|
|
|
|
(safi == SAFI_MPLS_VPN)) {
|
|
|
|
/* initialize syncrdpos to the first
|
|
|
|
* mid-layer table entry
|
|
|
|
*/
|
|
|
|
if (!bmp->syncrdpos) {
|
|
|
|
bmp->syncrdpos = bgp_table_top(table);
|
|
|
|
if (!bmp->syncrdpos)
|
|
|
|
goto eor;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look for a valid mid-layer table */
|
|
|
|
do {
|
|
|
|
table = bgp_dest_get_bgp_table_info(bmp->syncrdpos);
|
|
|
|
if (table) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bmp->syncrdpos = bgp_route_next(bmp->syncrdpos);
|
|
|
|
} while (bmp->syncrdpos);
|
|
|
|
|
|
|
|
/* mid-layer table completed */
|
|
|
|
if (!bmp->syncrdpos)
|
|
|
|
goto eor;
|
|
|
|
}
|
|
|
|
|
|
|
|
bn = bgp_node_lookup(table, &bmp->syncpos);
|
|
|
|
do {
|
|
|
|
if (!bn) {
|
|
|
|
bn = bgp_table_get_next(table, &bmp->syncpos);
|
|
|
|
if (!bn) {
|
|
|
|
if ((afi == AFI_L2VPN && safi == SAFI_EVPN) ||
|
|
|
|
(safi == SAFI_MPLS_VPN)) {
|
|
|
|
/* reset bottom-layer pointer */
|
|
|
|
memset(&bmp->syncpos, 0,
|
|
|
|
sizeof(bmp->syncpos));
|
|
|
|
bmp->syncpos.family = afi2family(afi);
|
|
|
|
/* check whethere there is a valid
|
|
|
|
* next mid-layer table, otherwise
|
|
|
|
* declare table completed (eor)
|
|
|
|
*/
|
|
|
|
for (bmp->syncrdpos = bgp_route_next(
|
|
|
|
bmp->syncrdpos);
|
|
|
|
bmp->syncrdpos;
|
|
|
|
bmp->syncrdpos = bgp_route_next(
|
|
|
|
bmp->syncrdpos))
|
|
|
|
if (bgp_dest_get_bgp_table_info(
|
|
|
|
bmp->syncrdpos))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
eor:
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_eor_afi_safi(bmp, afi, safi,
|
|
|
|
peer_type_flag);
|
|
|
|
return true;
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
bmp->syncpeerid = 0;
|
|
|
|
prefix_copy(&bmp->syncpos, bgp_dest_get_prefix(bn));
|
|
|
|
}
|
|
|
|
|
|
|
|
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) &&
|
|
|
|
!CHECK_FLAG(bpiter->flags,
|
|
|
|
BGP_PATH_SELECTED))
|
|
|
|
continue;
|
|
|
|
if (bpiter->peer->qobj_node.nid
|
|
|
|
<= bmp->syncpeerid)
|
|
|
|
continue;
|
|
|
|
if (bpi && bpiter->peer->qobj_node.nid
|
|
|
|
> bpi->peer->qobj_node.nid)
|
|
|
|
continue;
|
|
|
|
bpi = bpiter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
<= bmp->syncpeerid)
|
|
|
|
continue;
|
|
|
|
if (adjin && adjiter->peer->qobj_node.nid
|
|
|
|
> adjin->peer->qobj_node.nid)
|
|
|
|
continue;
|
|
|
|
adjin = adjiter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bpi || adjin)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bn = NULL;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
if (adjin && bpi
|
|
|
|
&& adjin->peer->qobj_node.nid < bpi->peer->qobj_node.nid) {
|
|
|
|
bpi = NULL;
|
|
|
|
bmp->syncpeerid = adjin->peer->qobj_node.nid;
|
|
|
|
} else if (adjin && bpi
|
|
|
|
&& adjin->peer->qobj_node.nid > bpi->peer->qobj_node.nid) {
|
|
|
|
adjin = NULL;
|
|
|
|
bmp->syncpeerid = bpi->peer->qobj_node.nid;
|
|
|
|
} else if (bpi) {
|
|
|
|
bmp->syncpeerid = bpi->peer->qobj_node.nid;
|
|
|
|
} else if (adjin) {
|
|
|
|
bmp->syncpeerid = adjin->peer->qobj_node.nid;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct prefix *bn_p = bgp_dest_get_prefix(bn);
|
|
|
|
struct prefix_rd *prd = NULL;
|
|
|
|
if (((afi == AFI_L2VPN) && (safi == SAFI_EVPN)) ||
|
|
|
|
(safi == SAFI_MPLS_VPN))
|
|
|
|
prd = (struct prefix_rd *)bgp_dest_get_prefix(bmp->syncrdpos);
|
|
|
|
|
|
|
|
bpi_num_labels = BGP_PATH_INFO_NUM_LABELS(bpi);
|
|
|
|
|
|
|
|
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),
|
|
|
|
bpi_num_labels ? bpi->extra->labels->label : NULL,
|
|
|
|
bpi_num_labels);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if (bpi)
|
|
|
|
peer_type_flag = bmp_get_peer_type(bpi->peer);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_VALID) &&
|
|
|
|
CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY))
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, peer_type_flag, bn_p, prd, bpi->attr,
|
2025-02-05 10:03:58 +01:00
|
|
|
afi, safi, bpi->uptime,
|
2025-03-12 10:26:12 +01:00
|
|
|
bpi_num_labels ? bpi->extra->labels->label : NULL, bpi_num_labels);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-02-05 10:17:11 +01:00
|
|
|
if (adjin) {
|
|
|
|
adjin_num_labels = adjin->labels ? adjin->labels->num_labels : 0;
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_monitor(bmp, adjin->peer, 0, peer_type_flag, bn_p, prd, adjin->attr, afi, safi,
|
|
|
|
adjin->uptime, adjin_num_labels ? &adjin->labels->label[0] : NULL,
|
|
|
|
adjin_num_labels);
|
2025-02-05 10:17:11 +01:00
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (bn)
|
|
|
|
bgp_dest_unlock_node(bn);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = *queuepos_ptr;
|
|
|
|
if (!bqe)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*queuepos_ptr = bmp_qlist_next(list, bqe);
|
|
|
|
|
|
|
|
bqe->refcount--;
|
|
|
|
if (!bqe->refcount) {
|
|
|
|
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;
|
|
|
|
uint8_t bpi_num_labels;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
if (peer != peer->bgp->peer_self && !peer_established(peer->connection)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
/* 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;
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bn = bgp_safi_node_lookup(peer->bgp->rib[afi][safi], safi, &bqe->p, prd);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bpi_num_labels = BGP_PATH_INFO_NUM_LABELS(bpi);
|
|
|
|
|
|
|
|
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),
|
|
|
|
bpi_num_labels ? bpi->extra->labels->label : NULL,
|
|
|
|
bpi_num_labels);
|
|
|
|
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;
|
|
|
|
struct peer *peer;
|
|
|
|
struct bgp_dest *bn = NULL;
|
|
|
|
bool written = false;
|
2025-02-05 10:17:11 +01:00
|
|
|
uint8_t bpi_num_labels, adjin_num_labels;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type_flag;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
bqe = bmp_pull(bmp);
|
|
|
|
if (!bqe)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
afi_t afi = bqe->afi;
|
|
|
|
safi_t safi = bqe->safi;
|
|
|
|
|
|
|
|
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) {
|
|
|
|
zlog_info("bmp: skipping queued item for deleted peer");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!peer_established(peer->connection))
|
|
|
|
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;
|
2025-03-12 10:26:12 +01:00
|
|
|
bn = bgp_safi_node_lookup(peer->bgp->rib[afi][safi], safi, &bqe->p, prd);
|
|
|
|
|
|
|
|
peer_type_flag = bmp_get_peer_type(peer);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_POSTPOLICY)) {
|
|
|
|
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_VALID))
|
|
|
|
continue;
|
|
|
|
if (bpi->peer == peer)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bpi_num_labels = BGP_PATH_INFO_NUM_LABELS(bpi);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, peer_type_flag, &bqe->p, prd,
|
|
|
|
bpi ? bpi->attr : NULL, afi, safi, bpi ? bpi->uptime : monotime(NULL),
|
|
|
|
bpi_num_labels ? bpi->extra->labels->label : NULL, bpi_num_labels);
|
2025-02-05 10:03:58 +01:00
|
|
|
written = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CHECK_FLAG(bmp->targets->afimon[afi][safi], BMP_MON_PREPOLICY)) {
|
|
|
|
struct bgp_adj_in *adjin;
|
|
|
|
|
|
|
|
for (adjin = bn ? bn->adj_in : NULL; adjin;
|
|
|
|
adjin = adjin->next) {
|
|
|
|
if (adjin->peer == peer)
|
|
|
|
break;
|
|
|
|
}
|
2025-02-05 10:17:11 +01:00
|
|
|
adjin_num_labels = adjin && adjin->labels ? adjin->labels->num_labels : 0;
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_monitor(bmp, peer, 0, peer_type_flag, &bqe->p, prd, adjin ? adjin->attr : NULL,
|
|
|
|
afi, safi, adjin ? adjin->uptime : monotime(NULL),
|
2025-02-05 10:17:11 +01:00
|
|
|
adjin_num_labels ? &adjin->labels->label[0] : NULL, adjin_num_labels);
|
2025-02-05 10:03:58 +01:00
|
|
|
written = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (!bqe->refcount)
|
|
|
|
XFREE(MTYPE_BMP_QUEUE, bqe);
|
|
|
|
|
|
|
|
if (bn)
|
|
|
|
bgp_dest_unlock_node(bn);
|
|
|
|
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr)
|
|
|
|
{
|
2025-03-12 10:26:12 +01:00
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
switch(bmp->state) {
|
|
|
|
case BMP_PeerUp:
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_send_peerup_vrf(bmp);
|
2025-02-05 10:03:58 +01:00
|
|
|
bmp_send_peerup(bmp);
|
|
|
|
bmp->state = BMP_Run;
|
2025-03-12 10:26:12 +01:00
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bmp_update_syncro(bmp, afi, safi, NULL);
|
2025-02-05 10:03:58 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BMP_Run:
|
|
|
|
if (bmp_wrmirror(bmp, pullwr))
|
|
|
|
break;
|
|
|
|
if (bmp_wrqueue(bmp, pullwr))
|
|
|
|
break;
|
|
|
|
if (bmp_wrqueue_locrib(bmp, pullwr))
|
|
|
|
break;
|
|
|
|
if (bmp_wrsync(bmp, pullwr))
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof)
|
|
|
|
{
|
|
|
|
if (eof)
|
|
|
|
zlog_info("bmp[%s] disconnected", bmp->remote);
|
|
|
|
else
|
|
|
|
flog_warn(EC_LIB_SYSTEM_CALL, "bmp[%s] connection error: %s",
|
|
|
|
bmp->remote, strerror(errno));
|
|
|
|
|
|
|
|
bmp_close(bmp);
|
|
|
|
bmp_free(bmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_queue_entry *bqe, bqeref;
|
|
|
|
size_t refcount;
|
|
|
|
|
|
|
|
refcount = bmp_session_count(&bt->sessions);
|
|
|
|
if (refcount == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
memset(&bqeref, 0, sizeof(bqeref));
|
|
|
|
prefix_copy(&bqeref.p, bgp_dest_get_prefix(bn));
|
|
|
|
bqeref.peerid = peer->qobj_node.nid;
|
|
|
|
bqeref.afi = afi;
|
|
|
|
bqeref.safi = safi;
|
|
|
|
|
|
|
|
if ((afi == AFI_L2VPN && safi == SAFI_EVPN && bn->pdest) ||
|
|
|
|
(safi == SAFI_MPLS_VPN))
|
|
|
|
prefix_copy(&bqeref.rd,
|
|
|
|
(struct prefix_rd *)bgp_dest_get_prefix(bn->pdest));
|
|
|
|
|
|
|
|
bqe = bmp_qhash_find(updhash, &bqeref);
|
|
|
|
if (bqe) {
|
|
|
|
if (bqe->refcount >= refcount)
|
|
|
|
/* nothing to do here */
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
bmp_qlist_del(updlist, bqe);
|
|
|
|
} else {
|
|
|
|
bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe));
|
|
|
|
memcpy(bqe, &bqeref, sizeof(*bqe));
|
|
|
|
|
|
|
|
bmp_qhash_add(updhash, bqe);
|
|
|
|
}
|
|
|
|
|
|
|
|
bqe->refcount = refcount;
|
|
|
|
bmp_qlist_add_tail(updlist, 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,
|
|
|
|
struct bgp_dest *bn, struct peer *peer, bool withdraw)
|
|
|
|
{
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_bgp *bmpbgp;
|
2025-02-05 10:03:58 +01:00
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp *bmp;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct listnode *node;
|
|
|
|
struct bmp_queue_entry *last_item;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (frrtrace_enabled(frr_bgp, bmp_process)) {
|
|
|
|
char pfxprint[PREFIX2STR_BUFFER];
|
|
|
|
|
|
|
|
prefix2str(&bn->rn->p, pfxprint, sizeof(pfxprint));
|
|
|
|
frrtrace(5, frr_bgp, bmp_process, peer, pfxprint, afi, safi,
|
|
|
|
withdraw);
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
2025-02-05 10:03:58 +01:00
|
|
|
continue;
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
/* 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;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
if (bgp_vrf != peer->bgp && !bmp_imported_bgp_find(bt, peer->bgp->name))
|
|
|
|
continue;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
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;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
if (!bmp->queuepos)
|
|
|
|
bmp->queuepos = last_item;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
pullwr_bump(bmp->pullwr);
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_nht_path_valid(struct bgp *bgp, struct bgp_path_info *path, bool valid)
|
|
|
|
{
|
|
|
|
struct bgp_dest *dest = path->net;
|
|
|
|
struct bgp_table *table;
|
|
|
|
|
|
|
|
if (frrtrace_enabled(frr_bgp, bmp_nht_path_valid)) {
|
|
|
|
char pfxprint[PREFIX2STR_BUFFER];
|
|
|
|
|
|
|
|
prefix2str(&dest->rn->p, pfxprint, sizeof(pfxprint));
|
|
|
|
frrtrace(4, frr_bgp, bmp_nht_path_valid, bgp, pfxprint, path, valid);
|
|
|
|
}
|
|
|
|
if (bgp->peer_self == path->peer)
|
|
|
|
/* self declared networks or redistributed networks are not relevant for bmp */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
table = bgp_dest_table(dest);
|
|
|
|
|
|
|
|
return bmp_process(bgp, table->afi, table->safi, dest, path->peer, !valid);
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
|
|
|
stream_putw(s, type);
|
|
|
|
stream_putw(s, 4);
|
|
|
|
stream_putl(s, value);
|
|
|
|
(*cnt)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_stat_put_u64(struct stream *s, size_t *cnt, uint16_t type,
|
|
|
|
uint64_t value)
|
|
|
|
{
|
|
|
|
stream_putw(s, type);
|
|
|
|
stream_putw(s, 8);
|
|
|
|
stream_putq(s, value);
|
|
|
|
(*cnt)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_stats(struct event *thread)
|
|
|
|
{
|
|
|
|
struct bmp_targets *bt = EVENT_ARG(thread);
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bgp *bgp;
|
|
|
|
|
|
|
|
if (bt->stat_msec)
|
|
|
|
event_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, &bt->t_stats);
|
|
|
|
|
|
|
|
bmp_stats_per_instance(bt->bgp, bt);
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib) {
|
|
|
|
bgp = bgp_lookup_by_name(bib->name);
|
|
|
|
if (bgp)
|
|
|
|
bmp_stats_per_instance(bgp, bt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_stats_per_instance(struct bgp *bgp, struct bmp_targets *bt)
|
|
|
|
{
|
2025-02-05 10:03:58 +01:00
|
|
|
struct stream *s;
|
|
|
|
struct peer *peer;
|
|
|
|
struct listnode *node;
|
|
|
|
struct timeval tv;
|
2025-03-12 10:26:12 +01:00
|
|
|
uint8_t peer_type_flag;
|
|
|
|
uint64_t peer_distinguisher = 0;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
/* Walk down all peers */
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
|
2025-02-05 10:03:58 +01:00
|
|
|
size_t count = 0, count_pos, len;
|
|
|
|
|
|
|
|
if (!peer_established(peer->connection))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
s = stream_new(BGP_MAX_PACKET_SIZE);
|
|
|
|
bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT);
|
2025-03-12 10:26:12 +01:00
|
|
|
peer_type_flag = bmp_get_peer_type(peer);
|
|
|
|
if (bmp_get_peer_distinguisher(peer->bgp, AFI_UNSPEC, peer_type_flag,
|
|
|
|
&peer_distinguisher)) {
|
|
|
|
zlog_warn("skipping bmp message for peer %s: can't get peer distinguisher",
|
|
|
|
peer->host);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bmp_per_peer_hdr(s, bt->bgp, peer, 0, peer_type_flag, peer_distinguisher, &tv);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
count_pos = stream_get_endp(s);
|
|
|
|
stream_putl(s, 0);
|
|
|
|
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_PFX_REJECTED,
|
|
|
|
peer->stat_pfx_filter);
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ASPATH,
|
|
|
|
peer->stat_pfx_aspath_loop);
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ORIGINATOR,
|
|
|
|
peer->stat_pfx_originator_loop);
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_CLUSTER,
|
|
|
|
peer->stat_pfx_cluster_loop);
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_PFX_DUP_WITHDRAW,
|
|
|
|
peer->stat_pfx_dup_withdraw);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_UPD_7606_WITHDRAW, peer->stat_pfx_withdraw);
|
2025-02-05 10:03:58 +01:00
|
|
|
if (bt->stats_send_experimental)
|
|
|
|
bmp_stat_put_u32(s, &count, BMP_STATS_FRR_NH_INVALID,
|
|
|
|
peer->stat_pfx_nh_invalid);
|
|
|
|
bmp_stat_put_u64(s, &count, BMP_STATS_SIZE_ADJ_RIB_IN,
|
|
|
|
peer->stat_pfx_adj_rib_in);
|
|
|
|
bmp_stat_put_u64(s, &count, BMP_STATS_SIZE_LOC_RIB,
|
|
|
|
peer->stat_pfx_loc_rib);
|
|
|
|
|
|
|
|
stream_putl_at(s, count_pos, count);
|
|
|
|
|
|
|
|
len = stream_get_endp(s);
|
|
|
|
stream_putl_at(s, BMP_LENGTH_POS, len);
|
|
|
|
|
|
|
|
bmp_send_all(bt->bmpbgp, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read from the BMP socket to detect session termination */
|
|
|
|
static void bmp_read(struct event *t)
|
|
|
|
{
|
|
|
|
struct bmp *bmp = EVENT_ARG(t);
|
|
|
|
char buf[1024];
|
|
|
|
ssize_t n;
|
|
|
|
|
|
|
|
bmp->t_read = NULL;
|
|
|
|
|
|
|
|
n = read(bmp->socket, buf, sizeof(buf));
|
|
|
|
if (n >= 1) {
|
|
|
|
zlog_info("bmp[%s]: unexpectedly received %zu bytes", bmp->remote, n);
|
|
|
|
} else if (n == 0) {
|
|
|
|
/* the TCP session was terminated by the far end */
|
|
|
|
bmp_wrerr(bmp, NULL, true);
|
|
|
|
return;
|
|
|
|
} else if (!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) {
|
|
|
|
/* the TCP session experienced a fatal error, likely a timeout */
|
|
|
|
bmp_wrerr(bmp, NULL, false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
event_add_read(bm->master, bmp_read, bmp, bmp->socket, &bmp->t_read);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock)
|
|
|
|
{
|
|
|
|
union sockunion su, *sumem;
|
|
|
|
struct prefix p;
|
|
|
|
int on = 1;
|
|
|
|
struct access_list *acl = NULL;
|
|
|
|
enum filter_type ret;
|
|
|
|
char buf[SU_ADDRSTRLEN];
|
|
|
|
struct bmp *bmp;
|
|
|
|
|
|
|
|
sumem = sockunion_getpeername(bmp_sock);
|
|
|
|
if (!sumem) {
|
|
|
|
close(bmp_sock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(&su, sumem, sizeof(su));
|
|
|
|
sockunion_free(sumem);
|
|
|
|
|
|
|
|
set_nonblocking(bmp_sock);
|
|
|
|
set_cloexec(bmp_sock);
|
|
|
|
|
|
|
|
if (!sockunion2hostprefix(&su, &p)) {
|
|
|
|
close(bmp_sock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
acl = NULL;
|
|
|
|
switch (p.family) {
|
|
|
|
case AF_INET:
|
|
|
|
acl = access_list_lookup(AFI_IP, bt->acl_name);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
acl = access_list_lookup(AFI_IP6, bt->acl6_name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = FILTER_PERMIT;
|
|
|
|
if (acl) {
|
|
|
|
ret = access_list_apply(acl, &p);
|
|
|
|
}
|
|
|
|
|
|
|
|
sockunion2str(&su, buf, SU_ADDRSTRLEN);
|
|
|
|
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%u",
|
|
|
|
su.sa.sa_family == AF_INET
|
|
|
|
? ntohs(su.sin.sin_port)
|
|
|
|
: ntohs(su.sin6.sin6_port));
|
|
|
|
|
|
|
|
if (ret == FILTER_DENY) {
|
|
|
|
bt->cnt_aclrefused++;
|
|
|
|
zlog_info("bmp[%s] connection refused by access-list", buf);
|
|
|
|
close(bmp_sock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bt->cnt_accept++;
|
|
|
|
|
|
|
|
if (setsockopt(bmp_sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
|
|
|
|
flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt SO_KEEPALIVE: %s(%d)",
|
|
|
|
bmp_sock, safe_strerror(errno), errno);
|
|
|
|
if (setsockopt(bmp_sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
|
|
|
|
flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt TCP_NODELAY: %s(%d)",
|
|
|
|
bmp_sock, safe_strerror(errno), errno);
|
|
|
|
|
|
|
|
zlog_info("bmp[%s] connection established", buf);
|
|
|
|
|
|
|
|
/* Allocate new BMP structure and set up default values. */
|
|
|
|
bmp = bmp_new(bt, bmp_sock);
|
|
|
|
strlcpy(bmp->remote, buf, sizeof(bmp->remote));
|
|
|
|
|
|
|
|
bmp->state = BMP_PeerUp;
|
|
|
|
bmp->pullwr = pullwr_new(bm->master, bmp_sock, bmp, bmp_wrfill,
|
|
|
|
bmp_wrerr);
|
|
|
|
event_add_read(bm->master, bmp_read, bmp, bmp_sock, &bmp->t_read);
|
|
|
|
bmp_send_initiation(bmp);
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Accept BMP connection. */
|
|
|
|
static void bmp_accept(struct event *thread)
|
|
|
|
{
|
|
|
|
union sockunion su;
|
|
|
|
struct bmp_listener *bl = EVENT_ARG(thread);
|
|
|
|
int bmp_sock;
|
|
|
|
|
|
|
|
/* We continue hearing BMP socket. */
|
|
|
|
event_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept);
|
|
|
|
|
|
|
|
memset(&su, 0, sizeof(union sockunion));
|
|
|
|
|
|
|
|
/* We can handle IPv4 or IPv6 socket. */
|
|
|
|
bmp_sock = sockunion_accept(bl->sock, &su);
|
|
|
|
if (bmp_sock < 0) {
|
|
|
|
zlog_info("bmp: accept_sock failed: %s", safe_strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bmp_open(bl->targets, bmp_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_close(struct bmp *bmp)
|
|
|
|
{
|
|
|
|
struct bmp_queue_entry *bqe;
|
|
|
|
struct bmp_mirrorq *bmq;
|
|
|
|
|
|
|
|
EVENT_OFF(bmp->t_read);
|
|
|
|
|
|
|
|
if (bmp->active)
|
|
|
|
bmp_active_disconnected(bmp->active);
|
|
|
|
|
|
|
|
while ((bmq = bmp_pull_mirror(bmp)))
|
|
|
|
if (!bmq->refcount)
|
|
|
|
XFREE(MTYPE_BMP_MIRRORQ, bmq);
|
|
|
|
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);
|
|
|
|
close(bmp->socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct bmp_bgp dummy = { .bgp = bgp };
|
|
|
|
return bmp_bgph_find(&bmp_bgph, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp;
|
|
|
|
|
|
|
|
bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
if (bmpbgp)
|
|
|
|
return bmpbgp;
|
|
|
|
|
|
|
|
bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp));
|
|
|
|
bmpbgp->bgp = bgp;
|
2025-03-12 10:26:12 +01:00
|
|
|
bmpbgp->vrf_state = vrf_state_unknown;
|
2025-02-05 10:03:58 +01:00
|
|
|
bmpbgp->mirror_qsizelimit = ~0UL;
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_targets_init(&bmpbgp->targets);
|
2025-02-05 10:03:58 +01:00
|
|
|
bmp_mirrorq_init(&bmpbgp->mirrorq);
|
|
|
|
bmp_bgph_add(&bmp_bgph, bmpbgp);
|
|
|
|
|
|
|
|
return bmpbgp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_bgp_put(struct bmp_bgp *bmpbgp)
|
|
|
|
{
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp_listener *bl;
|
|
|
|
|
|
|
|
bmp_bgph_del(&bmp_bgph, bmpbgp);
|
|
|
|
|
|
|
|
frr_each_safe (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
frr_each_safe (bmp_listeners, &bt->listeners, bl)
|
|
|
|
bmp_listener_put(bl);
|
|
|
|
|
|
|
|
bmp_targets_put(bt);
|
|
|
|
}
|
|
|
|
|
|
|
|
bmp_mirrorq_fini(&bmpbgp->mirrorq);
|
|
|
|
XFREE(MTYPE_BMP, bmpbgp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_bgp_del(struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
|
|
|
|
if (bmpbgp)
|
|
|
|
bmp_bgp_put(bmpbgp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
|
|
|
|
{
|
|
|
|
struct peer *peer = bgp->peer_self;
|
|
|
|
uint16_t send_holdtime;
|
|
|
|
as_t local_as;
|
|
|
|
|
|
|
|
if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
|
|
|
|
send_holdtime = peer->holdtime;
|
|
|
|
else
|
|
|
|
send_holdtime = peer->bgp->default_holdtime;
|
|
|
|
|
|
|
|
/* local-as Change */
|
|
|
|
if (peer->change_local_as)
|
|
|
|
local_as = peer->change_local_as;
|
|
|
|
else
|
|
|
|
local_as = peer->local_as;
|
|
|
|
|
|
|
|
struct stream *s = bgp_open_make(peer, send_holdtime, local_as);
|
|
|
|
size_t open_len = stream_get_endp(s);
|
|
|
|
|
|
|
|
bbpeer->open_rx_len = open_len;
|
|
|
|
if (bbpeer->open_rx)
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len);
|
|
|
|
memcpy(bbpeer->open_rx, s->data, open_len);
|
|
|
|
|
|
|
|
bbpeer->open_tx_len = open_len;
|
|
|
|
if (bbpeer->open_tx)
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, open_len);
|
|
|
|
memcpy(bbpeer->open_tx, s->data, open_len);
|
|
|
|
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the vrf status of the bmpbgp struct for vrf peer up/down
|
|
|
|
*
|
|
|
|
* if force is unknown, use zebra vrf state
|
|
|
|
*
|
|
|
|
* returns true if state has changed
|
|
|
|
*/
|
|
|
|
bool bmp_bgp_update_vrf_status(enum bmp_vrf_state *vrf_state, struct bgp *bgp,
|
|
|
|
enum bmp_vrf_state force)
|
|
|
|
{
|
|
|
|
enum bmp_vrf_state old_state;
|
|
|
|
struct bmp_bgp_peer *bbpeer;
|
|
|
|
struct peer *peer;
|
|
|
|
struct vrf *vrf;
|
|
|
|
bool changed;
|
|
|
|
|
|
|
|
if (!vrf_state || !bgp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
old_state = *vrf_state;
|
|
|
|
|
|
|
|
vrf = bgp_vrf_lookup_by_instance_type(bgp);
|
|
|
|
*vrf_state = force != vrf_state_unknown ? force
|
|
|
|
: vrf_is_enabled(vrf) ? vrf_state_up
|
|
|
|
: vrf_state_down;
|
|
|
|
|
|
|
|
changed = old_state != *vrf_state;
|
|
|
|
if (changed) {
|
|
|
|
peer = bgp->peer_self;
|
|
|
|
if (*vrf_state == vrf_state_up) {
|
|
|
|
bbpeer = bmp_bgp_peer_get(peer);
|
|
|
|
bmp_bgp_peer_vrf(bbpeer, bgp);
|
|
|
|
} else {
|
|
|
|
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
|
|
|
|
if (bbpeer) {
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
|
|
|
|
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
|
|
|
|
bmp_peerh_del(&bmp_peerh, bbpeer);
|
|
|
|
XFREE(MTYPE_BMP_PEER, bbpeer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid)
|
|
|
|
{
|
|
|
|
struct bmp_bgp_peer dummy = { .peerid = peerid };
|
|
|
|
return bmp_peerh_find(&bmp_peerh, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer)
|
|
|
|
{
|
|
|
|
struct bmp_bgp_peer *bbpeer;
|
|
|
|
|
|
|
|
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
|
|
|
|
if (bbpeer)
|
|
|
|
return bbpeer;
|
|
|
|
|
|
|
|
bbpeer = XCALLOC(MTYPE_BMP_PEER, sizeof(*bbpeer));
|
|
|
|
bbpeer->peerid = peer->qobj_node.nid;
|
|
|
|
bmp_peerh_add(&bmp_peerh, bbpeer);
|
|
|
|
|
|
|
|
return bbpeer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_targets *bmp_targets_find1(struct bgp *bgp, const char *name)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
struct bmp_targets dummy;
|
|
|
|
|
|
|
|
if (!bmpbgp)
|
|
|
|
return NULL;
|
|
|
|
dummy.name = (char *)name;
|
|
|
|
return bmp_targets_find(&bmpbgp->targets, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name)
|
|
|
|
{
|
|
|
|
struct bmp_targets *bt;
|
2025-03-12 10:26:12 +01:00
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
bt = bmp_targets_find1(bgp, name);
|
|
|
|
if (bt)
|
|
|
|
return bt;
|
|
|
|
|
|
|
|
bt = XCALLOC(MTYPE_BMP_TARGETS, sizeof(*bt));
|
|
|
|
bt->name = XSTRDUP(MTYPE_BMP_TARGETSNAME, name);
|
|
|
|
bt->bgp = bgp;
|
|
|
|
bt->bmpbgp = bmp_bgp_get(bgp);
|
|
|
|
bt->stats_send_experimental = true;
|
2025-03-12 10:26:12 +01:00
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bt->bgp_request_sync[afi][safi] = false;
|
2025-02-05 10:03:58 +01:00
|
|
|
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);
|
2025-03-12 10:26:12 +01:00
|
|
|
bmp_imported_bgps_init(&bt->imported_bgps);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
QOBJ_REG(bt, bmp_targets);
|
|
|
|
bmp_targets_add(&bt->bmpbgp->targets, bt);
|
|
|
|
return bt;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static void bmp_imported_bgp_free(struct bmp_imported_bgp *bib)
|
|
|
|
{
|
|
|
|
if (bib->name)
|
|
|
|
XFREE(MTYPE_BMP_IMPORTED_BGP, bib->name);
|
|
|
|
XFREE(MTYPE_BMP_IMPORTED_BGP, bib);
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static void bmp_targets_put(struct bmp_targets *bt)
|
|
|
|
{
|
|
|
|
struct bmp *bmp;
|
|
|
|
struct bmp_active *ba;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_imported_bgp *bib;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
EVENT_OFF(bt->t_stats);
|
|
|
|
|
|
|
|
frr_each_safe (bmp_actives, &bt->actives, ba)
|
|
|
|
bmp_active_put(ba);
|
|
|
|
|
|
|
|
frr_each_safe(bmp_session, &bt->sessions, bmp) {
|
|
|
|
bmp_close(bmp);
|
|
|
|
bmp_free(bmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
bmp_targets_del(&bt->bmpbgp->targets, bt);
|
|
|
|
QOBJ_UNREG(bt);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each_safe (bmp_imported_bgps, &bt->imported_bgps, bib)
|
|
|
|
bmp_imported_bgp_free(bib);
|
|
|
|
|
|
|
|
bmp_imported_bgps_fini(&bt->imported_bgps);
|
2025-02-05 10:03:58 +01:00
|
|
|
bmp_listeners_fini(&bt->listeners);
|
|
|
|
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);
|
|
|
|
bmp_session_fini(&bt->sessions);
|
|
|
|
|
|
|
|
XFREE(MTYPE_BMP_TARGETSNAME, bt->name);
|
|
|
|
XFREE(MTYPE_BMP_TARGETS, bt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_listener *bmp_listener_find(struct bmp_targets *bt,
|
|
|
|
const union sockunion *su,
|
|
|
|
int port)
|
|
|
|
{
|
|
|
|
struct bmp_listener dummy;
|
|
|
|
dummy.addr = *su;
|
|
|
|
dummy.port = port;
|
|
|
|
return bmp_listeners_find(&bt->listeners, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_listener *bmp_listener_get(struct bmp_targets *bt,
|
|
|
|
const union sockunion *su,
|
|
|
|
int port)
|
|
|
|
{
|
|
|
|
struct bmp_listener *bl = bmp_listener_find(bt, su, port);
|
|
|
|
|
|
|
|
if (bl)
|
|
|
|
return bl;
|
|
|
|
|
|
|
|
bl = XCALLOC(MTYPE_BMP_LISTENER, sizeof(*bl));
|
|
|
|
bl->targets = bt;
|
|
|
|
bl->addr = *su;
|
|
|
|
bl->port = port;
|
|
|
|
bl->sock = -1;
|
|
|
|
|
|
|
|
bmp_listeners_add(&bt->listeners, bl);
|
|
|
|
return bl;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static struct bmp_imported_bgp *bmp_imported_bgp_find(struct bmp_targets *bt, char *name)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp dummy;
|
|
|
|
|
|
|
|
dummy.name = name;
|
|
|
|
return bmp_imported_bgps_find(&bt->imported_bgps, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_send_all_bgp(struct peer *peer, bool down)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp);
|
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct listnode *node;
|
|
|
|
struct stream *s = NULL;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
|
|
|
|
s = bmp_peerstate(peer, down);
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (bmpbgp) {
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt)
|
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
}
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
if (bgp_vrf == peer->bgp || !bmp_imported_bgp_find(bt, peer->bgp->name))
|
|
|
|
continue;
|
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stream_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_imported_bgp_put(struct bmp_targets *bt, struct bmp_imported_bgp *bib)
|
|
|
|
{
|
|
|
|
bmp_imported_bgps_del(&bt->imported_bgps, bib);
|
|
|
|
bmp_imported_bgp_free(bib);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_imported_bgp *bmp_imported_bgp_get(struct bmp_targets *bt, char *name)
|
|
|
|
{
|
|
|
|
struct bmp_imported_bgp *bib = bmp_imported_bgp_find(bt, name);
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
if (bib)
|
|
|
|
return bib;
|
|
|
|
|
|
|
|
bib = XCALLOC(MTYPE_BMP_IMPORTED_BGP, sizeof(*bib));
|
|
|
|
if (name)
|
|
|
|
bib->name = XSTRDUP(MTYPE_BMP_IMPORTED_BGP, name);
|
|
|
|
bib->vrf_state = vrf_state_unknown;
|
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bib->bgp_request_sync[afi][safi] = false;
|
|
|
|
|
|
|
|
bib->targets = bt;
|
|
|
|
bmp_imported_bgps_add(&bt->imported_bgps, bib);
|
|
|
|
|
|
|
|
return bib;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static void bmp_listener_start(struct bmp_listener *bl)
|
|
|
|
{
|
|
|
|
int sock, ret;
|
|
|
|
|
|
|
|
sock = socket(bl->addr.sa.sa_family, SOCK_STREAM, 0);
|
|
|
|
if (sock < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sockopt_reuseaddr(sock);
|
|
|
|
sockopt_reuseport(sock);
|
|
|
|
sockopt_v6only(bl->addr.sa.sa_family, sock);
|
|
|
|
set_cloexec(sock);
|
|
|
|
|
|
|
|
ret = sockunion_bind(sock, &bl->addr, bl->port, &bl->addr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sock;
|
|
|
|
|
|
|
|
ret = listen(sock, 3);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_sock;
|
|
|
|
|
|
|
|
bl->sock = sock;
|
|
|
|
event_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept);
|
|
|
|
return;
|
|
|
|
out_sock:
|
|
|
|
close(sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_listener_stop(struct bmp_listener *bl)
|
|
|
|
{
|
|
|
|
EVENT_OFF(bl->t_accept);
|
|
|
|
|
|
|
|
if (bl->sock != -1)
|
|
|
|
close(bl->sock);
|
|
|
|
bl->sock = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_active *bmp_active_find(struct bmp_targets *bt,
|
|
|
|
const char *hostname, int port)
|
|
|
|
{
|
|
|
|
struct bmp_active dummy;
|
|
|
|
dummy.hostname = (char *)hostname;
|
|
|
|
dummy.port = port;
|
|
|
|
return bmp_actives_find(&bt->actives, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bmp_active *bmp_active_get(struct bmp_targets *bt,
|
|
|
|
const char *hostname, int port)
|
|
|
|
{
|
|
|
|
struct bmp_active *ba;
|
|
|
|
|
|
|
|
ba = bmp_active_find(bt, hostname, port);
|
|
|
|
if (ba)
|
|
|
|
return ba;
|
|
|
|
|
|
|
|
ba = XCALLOC(MTYPE_BMP_ACTIVE, sizeof(*ba));
|
|
|
|
ba->targets = bt;
|
|
|
|
ba->hostname = XSTRDUP(MTYPE_TMP, hostname);
|
|
|
|
ba->port = port;
|
|
|
|
ba->minretry = BMP_DFLT_MINRETRY;
|
|
|
|
ba->maxretry = BMP_DFLT_MAXRETRY;
|
|
|
|
ba->socket = -1;
|
|
|
|
|
|
|
|
bmp_actives_add(&bt->actives, ba);
|
|
|
|
return ba;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_put(struct bmp_active *ba)
|
|
|
|
{
|
|
|
|
EVENT_OFF(ba->t_timer);
|
|
|
|
EVENT_OFF(ba->t_read);
|
|
|
|
EVENT_OFF(ba->t_write);
|
|
|
|
|
|
|
|
bmp_actives_del(&ba->targets->actives, ba);
|
|
|
|
|
|
|
|
if (ba->bmp) {
|
|
|
|
ba->bmp->active = NULL;
|
|
|
|
bmp_close(ba->bmp);
|
|
|
|
bmp_free(ba->bmp);
|
|
|
|
}
|
|
|
|
if (ba->socket != -1)
|
|
|
|
close(ba->socket);
|
|
|
|
|
|
|
|
XFREE(MTYPE_TMP, ba->ifsrc);
|
|
|
|
XFREE(MTYPE_TMP, ba->hostname);
|
|
|
|
XFREE(MTYPE_BMP_ACTIVE, ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_setup(struct bmp_active *ba);
|
|
|
|
|
|
|
|
static void bmp_active_connect(struct bmp_active *ba)
|
|
|
|
{
|
|
|
|
enum connect_result res;
|
|
|
|
struct interface *ifp;
|
|
|
|
vrf_id_t vrf_id = VRF_DEFAULT;
|
|
|
|
int res_bind;
|
|
|
|
|
|
|
|
for (; ba->addrpos < ba->addrtotal; ba->addrpos++) {
|
|
|
|
if (ba->ifsrc) {
|
|
|
|
if (ba->targets && ba->targets->bgp)
|
|
|
|
vrf_id = ba->targets->bgp->vrf_id;
|
|
|
|
|
|
|
|
/* find interface and related */
|
|
|
|
/* address with same family */
|
|
|
|
ifp = if_lookup_by_name(ba->ifsrc, vrf_id);
|
|
|
|
if (!ifp) {
|
|
|
|
zlog_warn("bmp[%s]: failed to find interface",
|
|
|
|
ba->ifsrc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bgp_update_address(ifp, &ba->addrs[ba->addrpos],
|
|
|
|
&ba->addrsrc)){
|
|
|
|
zlog_warn("bmp[%s]: failed to find matching address",
|
|
|
|
ba->ifsrc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
zlog_info("bmp[%s]: selected source address : %pSU",
|
|
|
|
ba->ifsrc, &ba->addrsrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
ba->socket = sockunion_socket(&ba->addrs[ba->addrpos]);
|
|
|
|
if (ba->socket < 0) {
|
|
|
|
zlog_warn("bmp[%s]: failed to create socket",
|
|
|
|
ba->hostname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_nonblocking(ba->socket);
|
|
|
|
|
|
|
|
if (!sockunion_is_null(&ba->addrsrc)) {
|
|
|
|
res_bind = sockunion_bind(ba->socket, &ba->addrsrc, 0,
|
|
|
|
&ba->addrsrc);
|
|
|
|
if (res_bind < 0) {
|
|
|
|
zlog_warn(
|
|
|
|
"bmp[%s]: no bind currently to source address %pSU:%d",
|
|
|
|
ba->hostname, &ba->addrsrc, ba->port);
|
|
|
|
close(ba->socket);
|
|
|
|
ba->socket = -1;
|
|
|
|
sockunion_init(&ba->addrsrc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res = sockunion_connect(ba->socket, &ba->addrs[ba->addrpos],
|
|
|
|
htons(ba->port), 0);
|
|
|
|
switch (res) {
|
|
|
|
case connect_error:
|
|
|
|
zlog_warn("bmp[%s]: failed to connect to %pSU:%d",
|
|
|
|
ba->hostname, &ba->addrs[ba->addrpos],
|
|
|
|
ba->port);
|
|
|
|
close(ba->socket);
|
|
|
|
ba->socket = -1;
|
|
|
|
sockunion_init(&ba->addrsrc);
|
|
|
|
continue;
|
|
|
|
case connect_success:
|
|
|
|
zlog_info("bmp[%s]: connected to %pSU:%d",
|
|
|
|
ba->hostname, &ba->addrs[ba->addrpos],
|
|
|
|
ba->port);
|
|
|
|
break;
|
|
|
|
case connect_in_progress:
|
|
|
|
zlog_warn("bmp[%s]: connect in progress %pSU:%d",
|
|
|
|
ba->hostname, &ba->addrs[ba->addrpos],
|
|
|
|
ba->port);
|
|
|
|
bmp_active_setup(ba);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exhausted all addresses */
|
|
|
|
ba->curretry += ba->curretry / 2;
|
|
|
|
bmp_active_setup(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_resolved(struct resolver_query *resq, const char *errstr,
|
|
|
|
int numaddrs, union sockunion *addr)
|
|
|
|
{
|
|
|
|
struct bmp_active *ba = container_of(resq, struct bmp_active, resq);
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
if (numaddrs <= 0) {
|
|
|
|
zlog_warn("bmp[%s]: hostname resolution failed: %s",
|
|
|
|
ba->hostname, errstr);
|
|
|
|
ba->last_err = errstr;
|
|
|
|
ba->curretry += ba->curretry / 2;
|
|
|
|
ba->addrpos = 0;
|
|
|
|
ba->addrtotal = 0;
|
|
|
|
bmp_active_setup(ba);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (numaddrs > (int)array_size(ba->addrs))
|
|
|
|
numaddrs = array_size(ba->addrs);
|
|
|
|
|
|
|
|
ba->addrpos = 0;
|
|
|
|
ba->addrtotal = numaddrs;
|
|
|
|
for (i = 0; i < ba->addrtotal; i++)
|
|
|
|
memcpy(&ba->addrs[i], &addr[i], sizeof(ba->addrs[0]));
|
|
|
|
|
|
|
|
bmp_active_connect(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_thread(struct event *t)
|
|
|
|
{
|
|
|
|
struct bmp_active *ba = EVENT_ARG(t);
|
|
|
|
socklen_t slen;
|
|
|
|
int status, ret;
|
|
|
|
vrf_id_t vrf_id;
|
|
|
|
|
|
|
|
/* all 3 end up here, though only timer or read+write are active
|
|
|
|
* at a time */
|
|
|
|
EVENT_OFF(ba->t_timer);
|
|
|
|
EVENT_OFF(ba->t_read);
|
|
|
|
EVENT_OFF(ba->t_write);
|
|
|
|
|
|
|
|
ba->last_err = NULL;
|
|
|
|
|
|
|
|
if (ba->socket == -1) {
|
|
|
|
/* get vrf_id */
|
|
|
|
if (!ba->targets || !ba->targets->bgp)
|
|
|
|
vrf_id = VRF_DEFAULT;
|
|
|
|
else
|
|
|
|
vrf_id = ba->targets->bgp->vrf_id;
|
|
|
|
resolver_resolve(&ba->resq, AF_UNSPEC, vrf_id, ba->hostname,
|
|
|
|
bmp_active_resolved);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
slen = sizeof(status);
|
|
|
|
ret = getsockopt(ba->socket, SOL_SOCKET, SO_ERROR, (void *)&status,
|
|
|
|
&slen);
|
|
|
|
|
|
|
|
if (ret < 0 || status != 0) {
|
|
|
|
ba->last_err = strerror(status);
|
|
|
|
zlog_warn("bmp[%s]: failed to connect to %pSU:%d: %s",
|
|
|
|
ba->hostname, &ba->addrs[ba->addrpos], ba->port,
|
|
|
|
ba->last_err);
|
|
|
|
goto out_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
zlog_warn("bmp[%s]: outbound connection to %pSU:%d", ba->hostname,
|
|
|
|
&ba->addrs[ba->addrpos], ba->port);
|
|
|
|
|
|
|
|
ba->bmp = bmp_open(ba->targets, ba->socket);
|
|
|
|
if (!ba->bmp)
|
|
|
|
goto out_next;
|
|
|
|
|
|
|
|
ba->bmp->active = ba;
|
|
|
|
ba->socket = -1;
|
|
|
|
ba->curretry = ba->minretry;
|
|
|
|
return;
|
|
|
|
|
|
|
|
out_next:
|
|
|
|
close(ba->socket);
|
|
|
|
ba->socket = -1;
|
|
|
|
ba->addrpos++;
|
|
|
|
bmp_active_connect(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_disconnected(struct bmp_active *ba)
|
|
|
|
{
|
|
|
|
ba->bmp = NULL;
|
|
|
|
bmp_active_setup(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bmp_active_setup(struct bmp_active *ba)
|
|
|
|
{
|
|
|
|
EVENT_OFF(ba->t_timer);
|
|
|
|
EVENT_OFF(ba->t_read);
|
|
|
|
EVENT_OFF(ba->t_write);
|
|
|
|
|
|
|
|
if (ba->bmp)
|
|
|
|
return;
|
|
|
|
if (ba->resq.callback)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ba->curretry > ba->maxretry)
|
|
|
|
ba->curretry = ba->maxretry;
|
|
|
|
|
|
|
|
if (ba->socket == -1)
|
|
|
|
event_add_timer_msec(bm->master, bmp_active_thread, ba,
|
|
|
|
ba->curretry, &ba->t_timer);
|
|
|
|
else {
|
|
|
|
event_add_read(bm->master, bmp_active_thread, ba, ba->socket,
|
|
|
|
&ba->t_read);
|
|
|
|
event_add_write(bm->master, bmp_active_thread, ba, ba->socket,
|
|
|
|
&ba->t_write);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_node bmp_node = {
|
|
|
|
.name = "bmp",
|
|
|
|
.node = BMP_NODE,
|
|
|
|
.parent_node = BGP_NODE,
|
|
|
|
.prompt = "%s(config-bgp-bmp)# "
|
|
|
|
};
|
|
|
|
|
|
|
|
static void bmp_targets_autocomplete(vector comps, struct cmd_token *token)
|
|
|
|
{
|
|
|
|
struct bgp *bgp;
|
|
|
|
struct bmp_targets *target;
|
|
|
|
struct listnode *node;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
|
|
|
|
if (!bmpbgp)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
frr_each_safe (bmp_targets, &bmpbgp->targets, target)
|
|
|
|
vector_set(comps,
|
|
|
|
XSTRDUP(MTYPE_COMPLETION, target->name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct cmd_variable_handler bmp_targets_var_handlers[] = {
|
|
|
|
{.tokenname = "BMPTARGETS", .completions = bmp_targets_autocomplete},
|
|
|
|
{.completions = NULL}};
|
|
|
|
|
|
|
|
#define BMP_STR "BGP Monitoring Protocol\n"
|
|
|
|
|
|
|
|
#include "bgpd/bgp_bmp_clippy.c"
|
|
|
|
|
|
|
|
DEFPY_NOSH(bmp_targets_main,
|
|
|
|
bmp_targets_cmd,
|
|
|
|
"bmp targets BMPTARGETS",
|
|
|
|
BMP_STR
|
|
|
|
"Create BMP target group\n"
|
|
|
|
"Name of the BMP target group\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
|
|
|
|
bt = bmp_targets_get(bgp, bmptargets);
|
|
|
|
|
|
|
|
VTY_PUSH_CONTEXT_SUB(BMP_NODE, bt);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(no_bmp_targets_main,
|
|
|
|
no_bmp_targets_cmd,
|
|
|
|
"no bmp targets BMPTARGETS",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Delete BMP target group\n"
|
|
|
|
"Name of the BMP target group\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
|
|
|
|
bt = bmp_targets_find1(bgp, bmptargets);
|
|
|
|
if (!bt) {
|
|
|
|
vty_out(vty, "%% BMP target group not found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
bmp_targets_put(bt);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
DEFPY(bmp_import_vrf,
|
|
|
|
bmp_import_vrf_cmd,
|
|
|
|
"[no] bmp import-vrf-view VRFNAME$vrfname",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Import BMP information from another VRF\n"
|
|
|
|
"Specify the VRF or view instance name\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bgp *bgp;
|
|
|
|
struct bmp *bmp;
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
if (!bt->bgp) {
|
|
|
|
vty_out(vty, "%% BMP target, BGP instance not found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
if ((bt->bgp->name == NULL && vrfname == NULL) ||
|
|
|
|
(bt->bgp->name && vrfname && strmatch(vrfname, bt->bgp->name))) {
|
|
|
|
vty_out(vty, "%% BMP target, can not import our own BGP instance\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
if (no) {
|
|
|
|
bib = bmp_imported_bgp_find(bt, (char *)vrfname);
|
|
|
|
if (!bib) {
|
|
|
|
vty_out(vty, "%% BMP imported BGP instance not found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
bgp = bgp_lookup_by_name(bib->name);
|
|
|
|
if (!bgp)
|
|
|
|
return CMD_WARNING;
|
|
|
|
bmp_send_peerdown_vrf_per_instance(bt, bgp);
|
|
|
|
bmp_imported_bgp_put(bt, bib);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
bib = bmp_imported_bgp_find(bt, (char *)vrfname);
|
|
|
|
if (bib)
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
|
|
|
|
bib = bmp_imported_bgp_get(bt, (char *)vrfname);
|
|
|
|
bgp = bgp_lookup_by_name(bib->name);
|
|
|
|
if (!bgp)
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
if (bmp->state != BMP_PeerUp && bmp->state != BMP_Run)
|
|
|
|
continue;
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
bmp_send_peerup_vrf_per_instance(bmp, &bib->vrf_state, bgp);
|
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bmp_update_syncro(bmp, afi, safi, bgp);
|
|
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
DEFPY(bmp_listener_main,
|
|
|
|
bmp_listener_cmd,
|
|
|
|
"bmp listener <X:X::X:X|A.B.C.D> port (1-65535)",
|
|
|
|
BMP_STR
|
|
|
|
"Listen for inbound BMP connections\n"
|
|
|
|
"IPv6 address to listen on\n"
|
|
|
|
"IPv4 address to listen on\n"
|
|
|
|
"TCP Port number\n"
|
|
|
|
"TCP Port number\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp_listener *bl;
|
|
|
|
|
|
|
|
bl = bmp_listener_get(bt, listener, port);
|
|
|
|
if (bl->sock == -1)
|
|
|
|
bmp_listener_start(bl);
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(no_bmp_listener_main,
|
|
|
|
no_bmp_listener_cmd,
|
|
|
|
"no bmp listener <X:X::X:X|A.B.C.D> port (1-65535)",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Create BMP listener\n"
|
|
|
|
"IPv6 address to listen on\n"
|
|
|
|
"IPv4 address to listen on\n"
|
|
|
|
"TCP Port number\n"
|
|
|
|
"TCP Port number\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp_listener *bl;
|
|
|
|
|
|
|
|
bl = bmp_listener_find(bt, listener, port);
|
|
|
|
if (!bl) {
|
|
|
|
vty_out(vty, "%% BMP listener not found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
bmp_listener_stop(bl);
|
|
|
|
bmp_listener_put(bl);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_connect,
|
|
|
|
bmp_connect_cmd,
|
|
|
|
"[no] bmp connect HOSTNAME port (1-65535) {min-retry (100-86400000)|max-retry (100-86400000)} [source-interface <WORD$srcif>]",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Actively establish connection to monitoring station\n"
|
|
|
|
"Monitoring station hostname or address\n"
|
|
|
|
"TCP port\n"
|
|
|
|
"TCP port\n"
|
|
|
|
"Minimum connection retry interval\n"
|
|
|
|
"Minimum connection retry interval (milliseconds)\n"
|
|
|
|
"Maximum connection retry interval\n"
|
|
|
|
"Maximum connection retry interval (milliseconds)\n"
|
|
|
|
"Source interface to use\n"
|
|
|
|
"Define an interface\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp_active *ba;
|
|
|
|
|
|
|
|
if (no) {
|
|
|
|
ba = bmp_active_find(bt, hostname, port);
|
|
|
|
if (!ba) {
|
|
|
|
vty_out(vty, "%% No such active connection found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
/* connection deletion need same hostname port and interface */
|
|
|
|
if (ba->ifsrc || srcif)
|
|
|
|
if ((!ba->ifsrc) || (!srcif) ||
|
|
|
|
!strcmp(ba->ifsrc, srcif)) {
|
|
|
|
vty_out(vty,
|
|
|
|
"%% No such active connection found\n");
|
|
|
|
return CMD_WARNING;
|
|
|
|
}
|
|
|
|
bmp_active_put(ba);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ba = bmp_active_get(bt, hostname, port);
|
2025-02-05 10:19:11 +01:00
|
|
|
if (srcif) {
|
|
|
|
if (ba->ifsrc)
|
|
|
|
XFREE(MTYPE_TMP, ba->ifsrc);
|
2025-02-05 10:03:58 +01:00
|
|
|
ba->ifsrc = XSTRDUP(MTYPE_TMP, srcif);
|
2025-02-05 10:19:11 +01:00
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
if (min_retry_str)
|
|
|
|
ba->minretry = min_retry;
|
|
|
|
if (max_retry_str)
|
|
|
|
ba->maxretry = max_retry;
|
|
|
|
ba->curretry = ba->minretry;
|
|
|
|
bmp_active_setup(ba);
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_acl,
|
|
|
|
bmp_acl_cmd,
|
|
|
|
"[no] <ip|ipv6>$af access-list ACCESSLIST_NAME$access_list",
|
|
|
|
NO_STR
|
|
|
|
IP_STR
|
|
|
|
IPV6_STR
|
|
|
|
"Access list to restrict BMP sessions\n"
|
|
|
|
"Access list name\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
char **what;
|
|
|
|
|
|
|
|
if (no)
|
|
|
|
access_list = NULL;
|
|
|
|
if (!strcmp(af, "ipv6"))
|
|
|
|
what = &bt->acl6_name;
|
|
|
|
else
|
|
|
|
what = &bt->acl_name;
|
|
|
|
|
|
|
|
XFREE(MTYPE_BMP_ACLNAME, *what);
|
|
|
|
if (access_list)
|
|
|
|
*what = XSTRDUP(MTYPE_BMP_ACLNAME, access_list);
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_stats_cfg,
|
|
|
|
bmp_stats_cmd,
|
|
|
|
"[no] bmp stats [interval (100-86400000)]",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Send BMP statistics messages\n"
|
|
|
|
"Specify BMP stats interval\n"
|
|
|
|
"Interval (milliseconds) to send BMP Stats in\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
|
|
|
|
EVENT_OFF(bt->t_stats);
|
|
|
|
if (no)
|
|
|
|
bt->stat_msec = 0;
|
|
|
|
else if (interval_str)
|
|
|
|
bt->stat_msec = interval;
|
|
|
|
else
|
|
|
|
bt->stat_msec = BMP_STAT_DEFAULT_TIMER;
|
|
|
|
|
|
|
|
if (bt->stat_msec)
|
|
|
|
event_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec,
|
|
|
|
&bt->t_stats);
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_stats_send_experimental,
|
|
|
|
bmp_stats_send_experimental_cmd,
|
|
|
|
"[no] bmp stats send-experimental",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Send BMP statistics messages\n"
|
|
|
|
"Send experimental BMP stats [65531-65534]\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
|
|
|
|
bt->stats_send_experimental = !no;
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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 after decision process is applied\n")
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
uint8_t flag, prev;
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp *bmp;
|
|
|
|
|
|
|
|
argv_find_and_parse_afi(argv, argc, &index, &afi);
|
|
|
|
argv_find_and_parse_safi(argv, argc, &index, &safi);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
prev = bt->afimon[afi][safi];
|
|
|
|
if (no)
|
2025-02-05 10:17:11 +01:00
|
|
|
UNSET_FLAG(bt->afimon[afi][safi], flag);
|
2025-02-05 10:03:58 +01:00
|
|
|
else
|
2025-02-05 10:17:11 +01:00
|
|
|
SET_FLAG(bt->afimon[afi][safi], flag);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
if (prev == bt->afimon[afi][safi])
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_session, &bt->sessions, bmp)
|
|
|
|
bmp_update_syncro(bmp, afi, safi, NULL);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_mirror_cfg,
|
|
|
|
bmp_mirror_cmd,
|
|
|
|
"[no] bmp mirror",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Send BMP route mirroring messages\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
|
|
|
|
struct bmp *bmp;
|
|
|
|
|
|
|
|
if (bt->mirror == !no)
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
|
|
|
|
bt->mirror = !no;
|
|
|
|
if (bt->mirror)
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
struct bmp_mirrorq *bmq;
|
|
|
|
|
|
|
|
while ((bmq = bmp_pull_mirror(bmp)))
|
|
|
|
if (!bmq->refcount)
|
|
|
|
XFREE(MTYPE_BMP_MIRRORQ, bmq);
|
|
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(bmp_mirror_limit_cfg,
|
|
|
|
bmp_mirror_limit_cmd,
|
|
|
|
"bmp mirror buffer-limit (0-4294967294)",
|
|
|
|
BMP_STR
|
|
|
|
"Route Mirroring settings\n"
|
|
|
|
"Configure maximum memory used for buffered mirroring messages\n"
|
|
|
|
"Limit in bytes\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
|
|
|
struct bmp_bgp *bmpbgp;
|
|
|
|
|
|
|
|
bmpbgp = bmp_bgp_get(bgp);
|
|
|
|
bmpbgp->mirror_qsizelimit = buffer_limit;
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFPY(no_bmp_mirror_limit_cfg,
|
|
|
|
no_bmp_mirror_limit_cmd,
|
|
|
|
"no bmp mirror buffer-limit [(0-4294967294)]",
|
|
|
|
NO_STR
|
|
|
|
BMP_STR
|
|
|
|
"Route Mirroring settings\n"
|
|
|
|
"Configure maximum memory used for buffered mirroring messages\n"
|
|
|
|
"Limit in bytes\n")
|
|
|
|
{
|
|
|
|
VTY_DECLVAR_CONTEXT(bgp, bgp);
|
|
|
|
struct bmp_bgp *bmpbgp;
|
|
|
|
|
|
|
|
bmpbgp = bmp_bgp_get(bgp);
|
|
|
|
bmpbgp->mirror_qsizelimit = ~0UL;
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEFPY(show_bmp,
|
|
|
|
show_bmp_cmd,
|
|
|
|
"show bmp",
|
|
|
|
SHOW_STR
|
|
|
|
BMP_STR)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp_listener *bl;
|
|
|
|
struct bmp_active *ba;
|
|
|
|
struct bmp *bmp;
|
|
|
|
struct ttable *tt;
|
|
|
|
char uptime[BGP_UPTIME_LEN];
|
|
|
|
char *out;
|
|
|
|
|
|
|
|
frr_each(bmp_bgph, &bmp_bgph, bmpbgp) {
|
|
|
|
vty_out(vty, "BMP state for BGP %s:\n\n",
|
|
|
|
bmpbgp->bgp->name_pretty);
|
|
|
|
vty_out(vty, " Route Mirroring %9zu bytes (%zu messages) pending\n",
|
|
|
|
bmpbgp->mirror_qsize,
|
|
|
|
bmp_mirrorq_count(&bmpbgp->mirrorq));
|
|
|
|
vty_out(vty, " %9zu bytes maximum buffer used\n",
|
|
|
|
bmpbgp->mirror_qsizemax);
|
|
|
|
if (bmpbgp->mirror_qsizelimit != ~0UL)
|
|
|
|
vty_out(vty, " %9zu bytes buffer size limit\n",
|
|
|
|
bmpbgp->mirror_qsizelimit);
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
|
|
|
|
frr_each(bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
vty_out(vty, " Targets \"%s\":\n", bt->name);
|
|
|
|
vty_out(vty, " Route Mirroring %sabled\n",
|
|
|
|
bt->mirror ? "en" : "dis");
|
|
|
|
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
FOREACH_AFI_SAFI (afi, safi) {
|
|
|
|
|
|
|
|
uint8_t afimon_flag = bt->afimon[afi][safi];
|
|
|
|
|
|
|
|
if (!afimon_flag)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
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");
|
|
|
|
frr_each (bmp_listeners, &bt->listeners, bl)
|
|
|
|
vty_out(vty, " %pSU:%d\n", &bl->addr,
|
|
|
|
bl->port);
|
|
|
|
|
|
|
|
vty_out(vty, "\n Outbound connections:\n");
|
|
|
|
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
|
|
|
ttable_add_row(tt, "remote|state||timer|local");
|
|
|
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
|
|
|
frr_each (bmp_actives, &bt->actives, ba) {
|
|
|
|
const char *state_str = "?";
|
|
|
|
|
|
|
|
if (ba->bmp) {
|
|
|
|
peer_uptime(ba->bmp->t_up.tv_sec,
|
|
|
|
uptime, sizeof(uptime),
|
|
|
|
false, NULL);
|
|
|
|
ttable_add_row(tt,
|
|
|
|
"%s:%d|Up|%s|%s|%pSU",
|
|
|
|
ba->hostname, ba->port,
|
|
|
|
ba->bmp->remote, uptime,
|
|
|
|
&ba->addrsrc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptime[0] = '\0';
|
|
|
|
|
|
|
|
if (ba->t_timer) {
|
|
|
|
long trem = event_timer_remain_second(
|
|
|
|
ba->t_timer);
|
|
|
|
|
|
|
|
peer_uptime(monotime(NULL) - trem,
|
|
|
|
uptime, sizeof(uptime),
|
|
|
|
false, NULL);
|
|
|
|
state_str = "RetryWait";
|
|
|
|
} else if (ba->t_read) {
|
|
|
|
state_str = "Connecting";
|
|
|
|
} else if (ba->resq.callback) {
|
|
|
|
state_str = "Resolving";
|
|
|
|
}
|
|
|
|
|
|
|
|
ttable_add_row(tt, "%s:%d|%s|%s|%s|%pSU",
|
|
|
|
ba->hostname, ba->port,
|
|
|
|
state_str,
|
|
|
|
ba->last_err ? ba->last_err : "",
|
|
|
|
uptime, &ba->addrsrc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
out = ttable_dump(tt, "\n");
|
|
|
|
vty_out(vty, "%s", out);
|
2025-02-05 10:17:11 +01:00
|
|
|
XFREE(MTYPE_TMP_TTABLE, out);
|
2025-02-05 10:03:58 +01:00
|
|
|
ttable_del(tt);
|
|
|
|
|
|
|
|
vty_out(vty, "\n %zu connected clients:\n",
|
|
|
|
bmp_session_count(&bt->sessions));
|
|
|
|
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
|
|
|
ttable_add_row(tt, "remote|uptime|MonSent|MirrSent|MirrLost|ByteSent|ByteQ|ByteQKernel");
|
|
|
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
|
|
|
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
uint64_t total;
|
|
|
|
size_t q, kq;
|
|
|
|
|
|
|
|
pullwr_stats(bmp->pullwr, &total, &q, &kq);
|
|
|
|
|
|
|
|
peer_uptime(bmp->t_up.tv_sec, uptime,
|
|
|
|
sizeof(uptime), false, NULL);
|
|
|
|
|
|
|
|
ttable_add_row(tt, "%s|%s|%Lu|%Lu|%Lu|%Lu|%zu|%zu",
|
|
|
|
bmp->remote, uptime,
|
|
|
|
bmp->cnt_update,
|
|
|
|
bmp->cnt_mirror,
|
|
|
|
bmp->cnt_mirror_overruns,
|
|
|
|
total, q, kq);
|
|
|
|
}
|
|
|
|
out = ttable_dump(tt, "\n");
|
|
|
|
vty_out(vty, "%s", out);
|
2025-02-05 10:17:11 +01:00
|
|
|
XFREE(MTYPE_TMP_TTABLE, out);
|
2025-02-05 10:03:58 +01:00
|
|
|
ttable_del(tt);
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return CMD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_config_write(struct bgp *bgp, struct vty *vty)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct bmp_listener *bl;
|
|
|
|
struct bmp_active *ba;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_imported_bgp *bib;
|
2025-02-05 10:03:58 +01:00
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
if (!bmpbgp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (bmpbgp->mirror_qsizelimit != ~0UL)
|
|
|
|
vty_out(vty, " !\n bmp mirror buffer-limit %zu\n",
|
|
|
|
bmpbgp->mirror_qsizelimit);
|
|
|
|
|
|
|
|
frr_each(bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
vty_out(vty, " !\n bmp targets %s\n", bt->name);
|
|
|
|
|
|
|
|
if (bt->acl6_name)
|
|
|
|
vty_out(vty, " ipv6 access-list %s\n", bt->acl6_name);
|
|
|
|
if (bt->acl_name)
|
|
|
|
vty_out(vty, " ip access-list %s\n", bt->acl_name);
|
|
|
|
|
|
|
|
if (!bt->stats_send_experimental)
|
|
|
|
vty_out(vty, " no bmp stats send-experimental\n");
|
|
|
|
|
|
|
|
if (bt->stat_msec)
|
|
|
|
vty_out(vty, " bmp stats interval %d\n",
|
|
|
|
bt->stat_msec);
|
|
|
|
|
|
|
|
if (bt->mirror)
|
|
|
|
vty_out(vty, " bmp mirror\n");
|
|
|
|
|
|
|
|
FOREACH_AFI_SAFI (afi, safi) {
|
|
|
|
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 (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_lower(afi), safi2str(safi));
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib)
|
|
|
|
vty_out(vty, " bmp import-vrf-view %s\n",
|
|
|
|
bib->name ? bib->name : VRF_DEFAULT_NAME);
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
frr_each (bmp_listeners, &bt->listeners, bl)
|
2025-02-05 10:17:11 +01:00
|
|
|
vty_out(vty, " bmp listener %pSU port %d\n", &bl->addr, bl->port);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
frr_each (bmp_actives, &bt->actives, ba) {
|
|
|
|
vty_out(vty, " bmp connect %s port %u min-retry %u max-retry %u",
|
|
|
|
ba->hostname, ba->port,
|
|
|
|
ba->minretry, ba->maxretry);
|
|
|
|
|
|
|
|
if (ba->ifsrc)
|
|
|
|
vty_out(vty, " source-interface %s\n", ba->ifsrc);
|
|
|
|
else
|
|
|
|
vty_out(vty, "\n");
|
|
|
|
}
|
|
|
|
vty_out(vty, " exit\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgp_bmp_init(struct event_loop *tm)
|
|
|
|
{
|
|
|
|
install_node(&bmp_node);
|
|
|
|
install_default(BMP_NODE);
|
|
|
|
|
|
|
|
cmd_variable_handler_register(bmp_targets_var_handlers);
|
|
|
|
|
|
|
|
install_element(BGP_NODE, &bmp_targets_cmd);
|
|
|
|
install_element(BGP_NODE, &no_bmp_targets_cmd);
|
|
|
|
|
|
|
|
install_element(BMP_NODE, &bmp_listener_cmd);
|
|
|
|
install_element(BMP_NODE, &no_bmp_listener_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_connect_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_acl_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_stats_send_experimental_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_stats_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_monitor_cmd);
|
|
|
|
install_element(BMP_NODE, &bmp_mirror_cmd);
|
2025-03-12 10:26:12 +01:00
|
|
|
install_element(BMP_NODE, &bmp_import_vrf_cmd);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
install_element(BGP_NODE, &bmp_mirror_limit_cmd);
|
|
|
|
install_element(BGP_NODE, &no_bmp_mirror_limit_cmd);
|
|
|
|
|
|
|
|
install_element(VIEW_NODE, &show_bmp_cmd);
|
|
|
|
|
|
|
|
resolver_init(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_withdraw = old_route && !new_route;
|
|
|
|
struct bgp_path_info *updated_route =
|
|
|
|
is_withdraw ? old_route : new_route;
|
2025-03-12 10:26:12 +01:00
|
|
|
struct bmp_bgp *bmpbgp;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
int ret = 0;
|
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct listnode *node;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* this should never happen */
|
|
|
|
if (!updated_route) {
|
|
|
|
zlog_warn("%s: no updated route found!", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
if (!CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB))
|
|
|
|
continue;
|
|
|
|
if (bgp_vrf != bgp && !bmp_imported_bgp_find(bt, bgp->name))
|
|
|
|
continue;
|
|
|
|
ret = bmp_route_update_bgpbmp(bt, afi, safi, bn, old_route, new_route);
|
2025-02-05 10:03:58 +01:00
|
|
|
}
|
|
|
|
}
|
2025-03-12 10:26:12 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_route_update_bgpbmp(struct bmp_targets *bt, afi_t afi, safi_t safi,
|
|
|
|
struct bgp_dest *bn, struct bgp_path_info *old_route,
|
|
|
|
struct bgp_path_info *new_route)
|
|
|
|
{
|
|
|
|
bool is_withdraw = old_route && !new_route;
|
|
|
|
struct bgp_path_info *updated_route = is_withdraw ? old_route : new_route;
|
|
|
|
struct peer *peer = updated_route->peer;
|
|
|
|
struct bmp *bmp;
|
|
|
|
struct bmp_queue_entry *last_item;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
last_item = bmp_process_one(bt, &bt->locupdhash, &bt->locupdlist, bt->bgp, afi, safi, bn,
|
|
|
|
peer);
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
/* if bmp_process_one returns NULL
|
|
|
|
* we don't have anything to do next
|
|
|
|
*/
|
|
|
|
if (!last_item)
|
|
|
|
return 0;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
if (!bmp->locrib_queuepos)
|
|
|
|
bmp->locrib_queuepos = last_item;
|
2025-02-05 10:03:58 +01:00
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
pullwr_bump(bmp->pullwr);
|
2025-02-05 10:03:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bgp_bmp_early_fini(void)
|
|
|
|
{
|
|
|
|
resolver_terminate();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-12 10:26:12 +01:00
|
|
|
static int bmp_bgp_attribute_updated_instance(struct bmp_targets *bt, enum bmp_vrf_state *vrf_state,
|
|
|
|
struct bgp *bgp, bool withdraw, struct stream *s)
|
|
|
|
{
|
|
|
|
bmp_bgp_update_vrf_status(vrf_state, bgp, vrf_state_unknown);
|
|
|
|
if (*vrf_state == vrf_state_down)
|
|
|
|
/* do not send peer events, router id will not be enough to set state to up
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* vrf_state is up: trigger a peer event
|
|
|
|
*/
|
|
|
|
bmp_send_bt(bt, s);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called when the routerid of an instance changes */
|
|
|
|
static int bmp_bgp_attribute_updated(struct bgp *bgp, bool withdraw)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct listnode *node;
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
int ret = 0;
|
|
|
|
struct stream *s = bmp_peerstate(bgp->peer_self, withdraw);
|
|
|
|
struct bmp *bmp;
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (bmpbgp) {
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
ret = bmp_bgp_attribute_updated_instance(bt, &bmpbgp->vrf_state, bgp,
|
|
|
|
withdraw, s);
|
|
|
|
if (withdraw)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bmp_update_syncro(bmp, afi, safi, bgp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
if (bgp == bgp_vrf)
|
|
|
|
continue;
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib) {
|
|
|
|
if (bgp_lookup_by_name(bib->name) != bgp)
|
|
|
|
continue;
|
|
|
|
ret += bmp_bgp_attribute_updated_instance(bt, &bib->vrf_state, bgp,
|
|
|
|
withdraw, s);
|
|
|
|
if (withdraw)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
FOREACH_AFI_SAFI (afi, safi) {
|
|
|
|
bmp_update_syncro(bmp, afi, safi, bgp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stream_free(s);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_routerid_update(struct bgp *bgp, bool withdraw)
|
|
|
|
{
|
|
|
|
return bmp_bgp_attribute_updated(bgp, withdraw);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bmp_route_distinguisher_update(struct bgp *bgp, afi_t afi, bool preconfig)
|
|
|
|
{
|
|
|
|
return bmp_bgp_attribute_updated(bgp, preconfig);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _bmp_vrf_state_changed_internal(struct bgp *bgp, enum bmp_vrf_state vrf_state)
|
|
|
|
{
|
|
|
|
struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
|
|
|
|
struct bgp *bgp_vrf;
|
|
|
|
struct bmp_targets *bt;
|
|
|
|
struct listnode *node;
|
|
|
|
struct bmp_imported_bgp *bib;
|
|
|
|
struct bmp *bmp;
|
|
|
|
afi_t afi;
|
|
|
|
safi_t safi;
|
|
|
|
|
|
|
|
if (bmpbgp && bmp_bgp_update_vrf_status(&bmpbgp->vrf_state, bgp, vrf_state)) {
|
|
|
|
bmp_send_all_safe(bmpbgp, bmp_peerstate(bgp->peer_self,
|
|
|
|
bmpbgp->vrf_state == vrf_state_down));
|
|
|
|
if (vrf_state == vrf_state_up && bmpbgp->vrf_state == vrf_state_up) {
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bmp_update_syncro(bmp, afi, safi, bgp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
|
|
|
|
bmpbgp = bmp_bgp_find(bgp_vrf);
|
|
|
|
if (!bmpbgp)
|
|
|
|
continue;
|
|
|
|
if (bgp_vrf == bgp)
|
|
|
|
continue;
|
|
|
|
frr_each (bmp_targets, &bmpbgp->targets, bt) {
|
|
|
|
frr_each (bmp_imported_bgps, &bt->imported_bgps, bib) {
|
|
|
|
if (bgp_lookup_by_name(bib->name) != bgp)
|
|
|
|
continue;
|
|
|
|
if (bmp_bgp_update_vrf_status(&bib->vrf_state, bgp, vrf_state)) {
|
|
|
|
bmp_send_bt_safe(bt, bmp_peerstate(bgp->peer_self,
|
|
|
|
bib->vrf_state ==
|
|
|
|
vrf_state_down));
|
|
|
|
if (vrf_state == vrf_state_up &&
|
|
|
|
bib->vrf_state == vrf_state_up) {
|
|
|
|
frr_each (bmp_session, &bt->sessions, bmp) {
|
|
|
|
bmp_send_peerup_per_instance(bmp, bgp);
|
|
|
|
FOREACH_AFI_SAFI (afi, safi)
|
|
|
|
bmp_update_syncro(bmp, afi, safi,
|
|
|
|
bgp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called when a bgp instance goes up/down, implying that the underlying VRF
|
|
|
|
* has been created or deleted in zebra
|
|
|
|
*/
|
|
|
|
static int bmp_vrf_state_changed(struct bgp *bgp)
|
|
|
|
{
|
|
|
|
_bmp_vrf_state_changed_internal(bgp, vrf_state_unknown);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called when an interface goes up/down in a vrf, this may signal that the
|
|
|
|
* VRF changed state and is how bgp_snmp detects vrf state changes
|
|
|
|
*/
|
|
|
|
static int bmp_vrf_itf_state_changed(struct bgp *bgp, struct interface *itf)
|
|
|
|
{
|
|
|
|
enum bmp_vrf_state new_state;
|
|
|
|
|
|
|
|
/* if the update is not about the vrf device double-check
|
|
|
|
* the zebra status of the vrf
|
|
|
|
*/
|
|
|
|
if (!itf || !if_is_vrf(itf))
|
|
|
|
return bmp_vrf_state_changed(bgp);
|
|
|
|
|
|
|
|
new_state = if_is_up(itf) ? vrf_state_up : vrf_state_down;
|
|
|
|
_bmp_vrf_state_changed_internal(bgp, new_state);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-02-05 10:03:58 +01:00
|
|
|
static int bgp_bmp_module_init(void)
|
|
|
|
{
|
|
|
|
hook_register(bgp_packet_dump, bmp_mirror_packet);
|
|
|
|
hook_register(bgp_packet_send, bmp_outgoing_packet);
|
|
|
|
hook_register(peer_status_changed, bmp_peer_status_changed);
|
|
|
|
hook_register(peer_backward_transition, bmp_peer_backward);
|
|
|
|
hook_register(bgp_process, bmp_process);
|
2025-03-12 10:26:12 +01:00
|
|
|
hook_register(bgp_nht_path_update, bmp_nht_path_valid);
|
2025-02-05 10:03:58 +01:00
|
|
|
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);
|
|
|
|
hook_register(frr_early_fini, bgp_bmp_early_fini);
|
2025-03-12 10:26:12 +01:00
|
|
|
hook_register(bgp_instance_state, bmp_vrf_state_changed);
|
|
|
|
hook_register(bgp_vrf_status_changed, bmp_vrf_itf_state_changed);
|
|
|
|
hook_register(bgp_routerid_update, bmp_routerid_update);
|
|
|
|
hook_register(bgp_route_distinguisher_update, bmp_route_distinguisher_update);
|
2025-02-05 10:03:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FRR_MODULE_SETUP(.name = "bgpd_bmp", .version = FRR_VERSION,
|
|
|
|
.description = "bgpd BMP module",
|
|
|
|
.init = bgp_bmp_module_init,
|
|
|
|
);
|