summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_bmp.c267
-rw-r--r--bgpd/bgp_bmp.h24
-rw-r--r--bgpd/bgp_packet.c51
-rw-r--r--bgpd/bgp_packet.h1
-rw-r--r--bgpd/bgpd.c7
-rw-r--r--bgpd/bgpd.h1
6 files changed, 289 insertions, 62 deletions
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index 9d99c2c7fd..08f8b8b734 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -39,6 +39,8 @@
#include "bgpd/bgp_trace.h"
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
static void bmp_close(struct bmp *bmp);
static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp);
@@ -246,6 +248,33 @@ static void bmp_free(struct bmp *bmp)
#define BMP_PEER_TYPE_LOCAL_INSTANCE 2
#define BMP_PEER_TYPE_LOC_RIB_INSTANCE 3
+/* 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)
+{
+ if (peer->bgp->peer_self == peer)
+ return BMP_PEER_TYPE_LOC_RIB_INSTANCE;
+
+ return bmp_get_peer_type_vrf(peer->bgp->vrf_id);
+}
+
static inline int bmp_get_peer_distinguisher(struct bmp *bmp, afi_t afi,
uint8_t peer_type,
uint64_t *result_ref)
@@ -370,17 +399,18 @@ static void bmp_put_info_tlv(struct stream *s, uint16_t type,
stream_put(s, string, len);
}
-static void __attribute__((unused))
-bmp_put_vrftablename_info_tlv(struct stream *s, struct bmp *bmp)
+/* 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 bgp *bgp)
{
+ const char *vrftablename = "global";
#define BMP_INFO_TYPE_VRFTABLENAME 3
- const char *vrftablename = "global";
- if (bmp->targets->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
- struct vrf *vrf = vrf_lookup_by_id(bmp->targets->bgp->vrf_id);
- vrftablename = vrf ? vrf->name : NULL;
- }
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)
+ vrftablename = bgp->name;
+
if (vrftablename != NULL)
bmp_put_info_tlv(s, BMP_INFO_TYPE_VRFTABLENAME, vrftablename);
}
@@ -428,6 +458,10 @@ static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy)
+ sizeof(marker));
}
+/* 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
+ */
static struct stream *bmp_peerstate(struct peer *peer, bool down)
{
struct stream *s;
@@ -438,11 +472,14 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
uptime.tv_usec = 0;
monotime_to_realtime(&uptime, &uptime_real);
+ uint8_t peer_type = bmp_get_peer_type(peer);
+ bool is_locrib = peer_type == BMP_PEER_TYPE_LOC_RIB_INSTANCE;
+
#define BGP_BMP_MAX_PACKET_SIZE 1024
#define BMP_PEERUP_INFO_TYPE_STRING 0
s = stream_new(BGP_MAX_PACKET_SIZE);
- if (peer_established(peer->connection) && !down) {
+ if ((peer_established(peer->connection) || is_locrib) && !down) {
struct bmp_bgp_peer *bbpeer;
bmp_common_hdr(s, BMP_VERSION_3,
@@ -452,7 +489,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
&uptime_real);
/* Local Address (16 bytes) */
- if (peer->su_local->sa.sa_family == AF_INET6)
+ if (!peer->su_local || is_locrib)
+ stream_put(s, 0, 16);
+ else if (peer->su_local->sa.sa_family == AF_INET6)
stream_put(s, &peer->su_local->sin6.sin6_addr, 16);
else if (peer->su_local->sa.sa_family == AF_INET) {
stream_putl(s, 0);
@@ -462,15 +501,21 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
}
/* Local Port, Remote Port */
- if (peer->su_local->sa.sa_family == AF_INET6)
+ if (is_locrib)
+ stream_putw(s, 0);
+ else if (peer->su_local->sa.sa_family == AF_INET6)
stream_putw(s, htons(peer->su_local->sin6.sin6_port));
else if (peer->su_local->sa.sa_family == AF_INET)
stream_putw(s, htons(peer->su_local->sin.sin_port));
- if (peer->su_remote->sa.sa_family == AF_INET6)
+
+ if (is_locrib)
+ stream_putw(s, 0);
+ else if (peer->su_remote->sa.sa_family == AF_INET6)
stream_putw(s, htons(peer->su_remote->sin6.sin6_port));
else if (peer->su_remote->sa.sa_family == AF_INET)
stream_putw(s, htons(peer->su_remote->sin.sin_port));
+ /* TODO craft message with fields & capabilities for loc-rib */
static const uint8_t dummy_open[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -510,32 +555,40 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
type_pos = stream_get_endp(s);
stream_putc(s, 0); /* placeholder for down reason */
- 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;
+ 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;
+ }
}
stream_putc_at(s, type_pos, type);
}
+ if (is_locrib)
+ bmp_put_vrftablename_info_tlv(s, peer->bgp);
+
len = stream_get_endp(s);
stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
return s;
@@ -558,6 +611,25 @@ static int bmp_send_peerup(struct bmp *bmp)
return 0;
}
+static int bmp_send_peerup_vrf(struct bmp *bmp)
+{
+ struct bmp_bgp *bmpbgp = bmp->targets->bmpbgp;
+ 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(bmpbgp, vrf_state_unknown);
+
+ s = bmp_peerstate(bmpbgp->bgp->peer_self, bmpbgp->vrf_state == vrf_state_down);
+
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s);
+
+ return 0;
+}
+
+/* send a stream to all bmp sessions configured in a bgp instance */
/* XXX: kludge - filling the pullwr's buffer */
static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
{
@@ -570,6 +642,16 @@ static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
stream_free(s);
}
+static void bmp_send_all_safe(struct bmp_bgp *bmpbgp, struct stream *s)
+{
+ if (!bmpbgp) {
+ stream_free(s);
+ return;
+ }
+
+ bmp_send_all(bmpbgp, s);
+}
+
/*
* Route Mirroring
*/
@@ -816,7 +898,7 @@ static int bmp_peer_status_changed(struct peer *peer)
}
}
- bmp_send_all(bmpbgp, bmp_peerstate(peer, false));
+ bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, false));
return 0;
}
@@ -838,7 +920,7 @@ static int bmp_peer_backward(struct peer *peer)
bbpeer->open_rx_len = 0;
}
- bmp_send_all(bmpbgp, bmp_peerstate(peer, true));
+ bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, true));
return 0;
}
@@ -1476,6 +1558,7 @@ static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr)
{
switch(bmp->state) {
case BMP_PeerUp:
+ bmp_send_peerup_vrf(bmp);
bmp_send_peerup(bmp);
bmp->state = BMP_Run;
break;
@@ -1839,6 +1922,7 @@ static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp)
bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp));
bmpbgp->bgp = bgp;
+ bmpbgp->vrf_state = vrf_state_unknown;
bmpbgp->mirror_qsizelimit = ~0UL;
bmp_mirrorq_init(&bmpbgp->mirrorq);
bmp_bgph_add(&bmp_bgph, bmpbgp);
@@ -1873,6 +1957,79 @@ static int bmp_bgp_del(struct bgp *bgp)
return 0;
}
+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;
+ bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len);
+ memcpy(bbpeer->open_rx, s->data, open_len);
+
+ bbpeer->open_tx_len = open_len;
+ bbpeer->open_tx = bbpeer->open_rx;
+}
+
+/* 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(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force)
+{
+ enum bmp_vrf_state old_state;
+ struct bmp_bgp_peer *bbpeer;
+ struct peer *peer;
+ struct vrf *vrf;
+ struct bgp *bgp;
+ bool changed;
+
+ if (!bmpbgp || !bmpbgp->bgp)
+ return false;
+
+ bgp = bmpbgp->bgp;
+ old_state = bmpbgp->vrf_state;
+
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ bmpbgp->vrf_state = force != vrf_state_unknown ? force
+ : vrf_is_enabled(vrf) ? vrf_state_up
+ : vrf_state_down;
+
+ changed = old_state != bmpbgp->vrf_state;
+ if (changed) {
+ peer = bmpbgp->bgp->peer_self;
+ if (bmpbgp->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_rx);
+ bmp_peerh_del(&bmp_peerh, bbpeer);
+ XFREE(MTYPE_BMP_PEER, bbpeer);
+ }
+ }
+ }
+
+ return changed;
+}
+
static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid)
{
struct bmp_bgp_peer dummy = { .peerid = peerid };
@@ -2953,6 +3110,44 @@ static int bgp_bmp_early_fini(void)
return 0;
}
+/* 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)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+
+ if (!bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown))
+ return 1;
+
+ bmp_send_all_safe(bmpbgp,
+ bmp_peerstate(bgp->peer_self, bmpbgp->vrf_state == vrf_state_down));
+
+ 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)
+{
+ struct bmp_bgp *bmpbgp;
+ 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);
+
+ bmpbgp = bmp_bgp_find(bgp);
+ new_state = if_is_up(itf) ? vrf_state_up : vrf_state_down;
+ if (bmp_bgp_update_vrf_status(bmpbgp, new_state))
+ bmp_send_all(bmpbgp, bmp_peerstate(bgp->peer_self, new_state == vrf_state_down));
+
+ return 0;
+}
+
static int bgp_bmp_module_init(void)
{
hook_register(bgp_packet_dump, bmp_mirror_packet);
@@ -2965,6 +3160,8 @@ static int bgp_bmp_module_init(void)
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);
+ hook_register(bgp_instance_state, bmp_vrf_state_changed);
+ hook_register(bgp_vrf_status_changed, bmp_vrf_itf_state_changed);
return 0;
}
diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h
index 33247c4025..d45a4278f6 100644
--- a/bgpd/bgp_bmp.h
+++ b/bgpd/bgp_bmp.h
@@ -268,10 +268,19 @@ PREDECL_HASH(bmp_bgph);
#define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00
+enum bmp_vrf_state {
+ vrf_state_down = -1,
+ vrf_state_unknown = 0,
+ vrf_state_up = 1,
+};
+
struct bmp_bgp {
struct bmp_bgph_item bbi;
struct bgp *bgp;
+
+ enum bmp_vrf_state vrf_state;
+
struct bmp_targets_head targets;
struct bmp_mirrorq_head mirrorq;
@@ -280,12 +289,17 @@ struct bmp_bgp {
size_t mirror_qsizelimit;
};
+extern bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force);
+
enum {
- BMP_PEERDOWN_LOCAL_NOTIFY = 1,
- BMP_PEERDOWN_LOCAL_FSM = 2,
- BMP_PEERDOWN_REMOTE_NOTIFY = 3,
- BMP_PEERDOWN_REMOTE_CLOSE = 4,
- BMP_PEERDOWN_ENDMONITOR = 5,
+ /* RFC7854 - 10.8 */
+ BMP_PEERDOWN_LOCAL_NOTIFY = 1,
+ BMP_PEERDOWN_LOCAL_FSM = 2,
+ BMP_PEERDOWN_REMOTE_NOTIFY = 3,
+ BMP_PEERDOWN_REMOTE_CLOSE = 4,
+ BMP_PEERDOWN_ENDMONITOR = 5,
+ /* RFC9069 - 8.4 */
+ BMP_PEERDOWN_LOCAL_TLV = 6,
};
enum {
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 646ab1d95f..6b116db107 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -641,31 +641,11 @@ void bgp_keepalive_send(struct peer *peer)
bgp_writes_on(peer->connection);
}
-/*
- * Creates a BGP Open packet and appends it to the peer's output queue.
- * Sets capabilities as necessary.
- */
-void bgp_open_send(struct peer_connection *connection)
+struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as)
{
- struct stream *s;
- uint16_t send_holdtime;
- as_t local_as;
- struct peer *peer = connection->peer;
+ struct stream *s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
bool ext_opt_params = false;
- 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;
-
- s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
-
/* Make open packet. */
bgp_packet_set_marker(s, BGP_MSG_OPEN);
@@ -704,6 +684,33 @@ void bgp_open_send(struct peer_connection *connection)
ext_opt_params ? " (Extended)" : "", BGP_VERSION_4,
local_as, send_holdtime, &peer->local_id);
+ return s;
+}
+
+/*
+ * Creates a BGP Open packet and appends it to the peer's output queue.
+ * Sets capabilities as necessary.
+ */
+void bgp_open_send(struct peer_connection *connection)
+{
+ struct stream *s;
+ uint16_t send_holdtime;
+ as_t local_as;
+ struct peer *peer = connection->peer;
+
+ 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;
+
+ s = bgp_open_make(peer, send_holdtime, local_as);
+
/* Dump packet if debug option is set. */
/* bgp_packet_dump (s); */
hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s);
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index b67acf2055..c266b17266 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -43,6 +43,7 @@ DECLARE_HOOK(bgp_packet_send,
/* Packet send and receive function prototypes. */
extern void bgp_keepalive_send(struct peer *peer);
+extern struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as);
extern void bgp_open_send(struct peer_connection *connection);
extern void bgp_notify_send(struct peer_connection *connection, uint8_t code,
uint8_t sub_code);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 80b1ae39d4..a186243ffc 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -85,6 +85,7 @@ DEFINE_QOBJ_TYPE(bgp_master);
DEFINE_QOBJ_TYPE(bgp);
DEFINE_QOBJ_TYPE(peer);
DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
+DEFINE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
/* BGP process wide configuration. */
static struct bgp_master bgp_master;
@@ -3929,6 +3930,9 @@ void bgp_instance_up(struct bgp *bgp)
struct peer *peer;
struct listnode *node, *next;
+ /* notify BMP of instance state changed */
+ hook_call(bgp_instance_state, bgp);
+
bgp_set_redist_vrf_bitmaps(bgp, true);
/* Register with zebra. */
@@ -3957,6 +3961,9 @@ void bgp_instance_down(struct bgp *bgp)
/* Cleanup evpn instance state */
bgp_evpn_instance_down(bgp);
+ /* notify BMP of instance state changed */
+ hook_call(bgp_instance_state, bgp);
+
/* Stop timers. */
if (bgp->t_rmap_def_originate_eval)
EVENT_OFF(bgp->t_rmap_def_originate_eval);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 3c3655f0a5..852efdf19d 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -880,6 +880,7 @@ DECLARE_HOOK(bgp_snmp_traps_config_write, (struct vty *vty), (vty));
DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled),
(vrf, enabled));
+DECLARE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
/* Thread callback information */
struct afi_safi_info {