0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-12 22:28:44 +00:00
bird/proto/snmp/bgp4_mib.c
Vojtech Vilimek 4a500725a1 SNMP
The BIRD protocol SNMP makes it possible to retrieve management information
through SNMP. This is accomplished by implementing AgentX protocol. The BIRD
acts as an AgentX subagent, registers to master agent and provides management
information. Master agent handles SNMP communication and forwards request to
registered subagents. You will therefore need an additional component -- a SNMP
daemon capable of acting as AgentX master agent. In theory, the information
consumer don't have to support SNMP and could be very simple master agent for
logging/monitoring the BIRD state. For more detail see provided documentation.

This commit is squashed version of development history. Full development history
could be found on branch `proto-snmp'.
2024-08-21 22:31:52 +02:00

1010 lines
29 KiB
C

/*
* BIRD -- Simple Network Management Protocol (SNMP)
* BGP4-MIB bgpPeerTable
*
* (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
* (c) 2022 CZ.NIC z.s.p.o
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "snmp.h"
#include "snmp_utils.h"
#include "subagent.h"
#include "bgp4_mib.h"
#include "mib_tree.h"
#include "nest/cli.h"
/* hash table macros */
#define SNMP_HASH_KEY(n) n->peer_ip
#define SNMP_HASH_NEXT(n) n->next
#define SNMP_HASH_EQ(ip1, ip2) ip4_equal(ip1, ip2)
#define SNMP_HASH_FN(ip) ip4_hash(ip)
#define SNMP_HASH_LESS4(ip1, ip2) ip4_less(ip1, ip2)
#define SNMP_HASH_LESS6(ip1, ip2) ip6_less(ip1, ip2)
/* hash table only store ip4 addresses */
#define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2)
#define DECLARE_BGP4(addr, proto, conn, stats, config) \
ip4_addr addr; \
const struct bgp_proto UNUSED *proto; \
const struct bgp_conn UNUSED *conn; \
const struct bgp_stats UNUSED *stats; \
const struct bgp_config UNUSED *config
#define POPULATE_BGP4(addr, proto, conn, stats, config) populate_bgp4(c, &(addr), &(proto), &(conn), &(stats), &(config))
static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
static const STATIC_OID(2) bgp4_mib_oid = STATIC_OID_INITIALIZER(2, SNMP_MGMT, SNMP_MIB_2, SNMP_BGP4_MIB);
static inline void
snmp_hash_add_peer(struct snmp_proto *p, struct snmp_bgp_peer *peer)
{
HASH_INSERT(p->bgp_hash, SNMP_HASH, peer);
}
static inline struct snmp_bgp_peer *
snmp_hash_find(struct snmp_proto *p, ip4_addr key)
{
return HASH_FIND(p->bgp_hash, SNMP_HASH, key);
}
static inline void
snmp_bgp_last_error(const struct bgp_proto *bgp, char err[2])
{
err[0] = bgp->last_error_code & 0x00FF0000 >> 16;
err[1] = bgp->last_error_code & 0x000000FF;
}
static void
snmp_bgp_reg_failed(struct snmp_proto *p, const struct agentx_response *res, struct snmp_registration *reg)
{
(void) res;
(void) reg;
snmp_reset(p);
}
/*
* snmp_bgp_fsm_state - extract BGP FSM state for SNMP BGP4-MIB
* @bgp_proto: BGP instance
*
* Return FSM state in BGP4-MIB encoding
*/
static inline uint
snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto)
{
const struct bgp_conn *bgp_conn = bgp_proto->conn;
const struct bgp_conn *bgp_in = &bgp_proto->incoming_conn;
const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn;
if (bgp_conn)
return bgp_conn->state + 1;
if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE &&
MIN(bgp_in->state, bgp_out->state) != BS_CLOSE)
return MIN(bgp_in->state, bgp_out->state) + 1;
if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE)
return BS_IDLE;
return MAX(bgp_in->state, bgp_out->state) + 1;
}
/*
* snmp_bgp_notify_common - common functionaly for BGP4-MIB notifications
* @p: SNMP protocol instance
* @type: type of notification send - either established or backward transition
* @ip4: IPv4 remote addr
* @last_error: 2 bytes of BGP last error
* @state_val: BGP peer state as defined in MIB
*/
static void
snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_error[], uint state_val)
{
uint sz = (uint) (snmp_varbind_size_from_len(9, AGENTX_IP_ADDRESS, 0)
+ snmp_varbind_size_from_len(9, AGENTX_OCTET_STRING, 2)
+ snmp_varbind_size_from_len(9, AGENTX_INTEGER, 0));
u32 trap_ids[] = { 1, 0, type };
STATIC_ASSERT(ARRAY_SIZE(trap_ids) == 3);
/* additional size for trap identification, here either
* bgpEstablishedNotification or bgpBackwardTransNotification (see below) */
void *data = tmp_alloc(snmp_oid_size_from_len(ARRAY_SIZE(trap_ids)) + sz);
struct oid *head = data;
{ /* trap id BGP4-MIB::bgpEstablishedNotification (.1.3.6.1.2.15.0.1)
* or BGP4-MIB::bgpBackwardTransNotification (.1.3.6.1.2.15.0.2) */
head->n_subid = ARRAY_SIZE(trap_ids);
head->prefix = SNMP_MGMT;
head->include = head->reserved = 0;
for (uint i = 0; i < head->n_subid; i++)
head->ids[i] = trap_ids[i];
}
data += sz;
struct agentx_varbind *addr_vb = data;
struct agentx_varbind *error_vb = \
data + snmp_varbind_size_from_len(9, AGENTX_IP_ADDRESS, 0);
struct agentx_varbind *state_vb = \
(void *) error_vb + snmp_varbind_size_from_len(9, AGENTX_OCTET_STRING, 2);
u32 oid_ids[] = {
SNMP_MIB_2, SNMP_BGP4_MIB, BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY
};
/*
* The n_subid is 9 in all cases because all are rows entries of
* BGP4-MIB::bgpPeerTable
* BGP4-MIB::bgpPeerRemoteAddr = .1.3.6.1.[2].1.15.3.1.7.a.b.c.d
* where .1.3.6.1 is internet prefix, .[2] is SNMP_MGMT,
* .1.15.3.1.7.a.b.c.d has 9 elements (a.b.c.d are IP addr bytes)
* Here subidentifier 7 is entry type bgpPeerRemoteAddr.
*/
#define PEER_TABLE_ENTRY 9
#define ENTRY_TYPE 4
{ /* BGP4-MIB::bgpPeerRemoteAddr */
struct oid *addr = &addr_vb->name;
*addr = (struct oid) {
.n_subid = PEER_TABLE_ENTRY, .prefix = SNMP_MGMT, .include = 0,
.reserved = 0,
};
for (uint i = 0; i < ARRAY_SIZE(oid_ids); i++)
addr->ids[i] = oid_ids[i];
addr->ids[ENTRY_TYPE] = BGP4_MIB_REMOTE_ADDR;
ip4_to_oid(addr, ip4);
}
/* We have enough space inside the TX buffer prepared */
struct snmp_pdu dummy = { 0 };
dummy.sr_vb_start = addr_vb;
snmp_varbind_ip4(&dummy, ip4);
{ /* BGP4-MIB::bgpPeerLastError */
struct oid *error = &error_vb->name;
*error = (struct oid) {
.n_subid = PEER_TABLE_ENTRY, .prefix = SNMP_MGMT, .include = 0,
.reserved = 0,
};
for (uint i = 0; i < ARRAY_SIZE(oid_ids); i++)
error->ids[i] = oid_ids[i];
error->ids[ENTRY_TYPE] = BGP4_MIB_LAST_ERROR;
ip4_to_oid(error, ip4);
}
dummy.sr_vb_start = error_vb;
snmp_varbind_nstr(&dummy, last_error, 2);
{ /* BGP4-MIB::bgpPeerState */
struct oid *state = &state_vb->name;
*state = (struct oid) {
.n_subid = PEER_TABLE_ENTRY, .prefix = SNMP_MGMT, .include = 0,
.reserved = 0,
};
for (uint i = 0; i < ARRAY_SIZE(oid_ids); i++)
state->ids[i] = oid_ids[i];
state->ids[ENTRY_TYPE] = BGP4_MIB_STATE;
ip4_to_oid(state, ip4);
}
dummy.sr_vb_start = state_vb;
snmp_varbind_int(&dummy, state_val);
/* We do not send the systemUpTime.0 */
snmp_notify_pdu(p, head, data, sz, 0);
#undef OID_N_SUBID
}
static void
snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type)
{
/* possibly incorrect cast */
ip4_addr ip4 = ipa_to_ip4(bgp->remote_ip);
char last_error[2];
snmp_bgp_last_error(bgp, last_error);
uint state_val = snmp_bgp_fsm_state(bgp);
snmp_bgp_notify_common(p, type, ip4, last_error, state_val);
}
void UNUSED
snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp)
{
snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_ESTABLISHED_NOTIFICATION);
}
void UNUSED
snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp)
{
snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_BACKWARD_TRANS_NOTIFICATION);
}
static int
snmp_bgp_valid_ip4(const struct oid *o)
{
return snmp_valid_ip4_index(o, 5);
}
static inline ip4_addr
ip4_from_oid(const struct oid *o)
{
return ip4_build(
o->n_subid > 5 ? (o->ids[5] & 0xff) : 0,
o->n_subid > 6 ? (o->ids[6] & 0xff) : 0,
o->n_subid > 7 ? (o->ids[7] & 0xff) : 0,
o->n_subid > 8 ? (o->ids[8] & 0xff) : 0
);
}
static inline void
ip4_to_oid(struct oid *o, ip4_addr addr)
{
u32 tmp = ip4_to_u32(addr);
ASSUME(o->n_subid >= 9);
o->ids[5] = (tmp & 0xFF000000) >> 24;
o->ids[6] = (tmp & 0x00FF0000) >> 16;
o->ids[7] = (tmp & 0x0000FF00) >> 8;
o->ids[8] = (tmp & 0x000000FF) >> 0;
}
static inline enum snmp_search_res
populate_bgp4(struct snmp_pdu *c, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn
**conn, const struct bgp_stats **stats, const struct bgp_config **config)
{
const struct oid * const oid = &c->sr_vb_start->name;
if (snmp_bgp_valid_ip4(oid) && oid->n_subid == 9)
*addr = ip4_from_oid(oid);
else
return SNMP_SEARCH_NO_INSTANCE;
struct snmp_bgp_peer *pe = snmp_hash_find(c->p, *addr);
if (!pe)
return SNMP_SEARCH_NO_INSTANCE;
const struct bgp_proto *bgp_proto;
*proto = bgp_proto = pe->bgp_proto;
if (!ipa_is_ip4(bgp_proto->remote_ip))
{
c->error = AGENTX_RES_GEN_ERROR;
return SNMP_SEARCH_NO_INSTANCE;
}
ip4_addr proto_ip = ipa_to_ip4(bgp_proto->remote_ip);
if (!ip4_equal(proto_ip, pe->peer_ip))
{
/* Here, we could be in problem as the bgp_proto IP address could be changed */
c->error = AGENTX_RES_GEN_ERROR;
return SNMP_SEARCH_NO_INSTANCE;
}
*conn = bgp_proto->conn;
*stats = &bgp_proto->stats;
*config = bgp_proto->cf;
return SNMP_SEARCH_OK;
}
/*
*
* MIB tree fill hooks
*
*/
static enum snmp_search_res
fill_bgp_version(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
if (c->sr_vb_start->name.n_subid != 4)
return SNMP_SEARCH_NO_INSTANCE;
c->size -= snmp_str_size_from_len(1);
snmp_varbind_nstr(c, BGP4_VERSIONS, 1);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_local_as(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
if (c->sr_vb_start->name.n_subid != 4)
return SNMP_SEARCH_NO_INSTANCE;
snmp_varbind_int(c, c->p->bgp4_local_as);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_peer_id(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED)
// TODO last
snmp_varbind_ip4(c, ip4_from_u32(bgp_proto->remote_id));
else
snmp_varbind_ip4(c, IP4_NONE);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_peer_state(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
snmp_varbind_int(c, fsm_state);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_admin_status(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
if (bgp_proto->p.disabled)
snmp_varbind_int(c, BGP4_ADMIN_STOP);
else
snmp_varbind_int(c, BGP4_ADMIN_START);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_neg_version(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED)
snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_VALUE);
else
snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_local_addr(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->local_ip));
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_local_port(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, bgp_conf->local_port);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_remote_addr(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->remote_ip));
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_remote_port(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, bgp_conf->remote_port);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_remote_as(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, bgp_proto->remote_as);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_in_updates(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_counter32(c, bgp_stats->rx_updates);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_out_updates(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_counter32(c, bgp_stats->tx_updates);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_in_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_counter32(c, bgp_stats->rx_messages);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_out_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_counter32(c, bgp_stats->tx_messages);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_last_err(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
char last_error[2];
snmp_bgp_last_error(bgp_proto, last_error);
snmp_varbind_nstr(c, last_error, 2);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_established_trans(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_counter32(c,
bgp_stats->fsm_established_transitions);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_established_time(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_gauge32(c,
(current_time() - bgp_proto->last_established) TO_S);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_retry_interval(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, bgp_conf->connect_retry_time);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_hold_time(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, (bgp_conn) ? bgp_conn->hold_time : 0);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_keep_alive(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
if (!bgp_conf->hold_time)
snmp_varbind_int(c, 0);
else
snmp_varbind_int(c,
(bgp_conn) ? bgp_conn->keepalive_time : 0);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_hold_time_conf(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_int(c, bgp_conf->hold_time);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_keep_alive_conf(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
if (!bgp_conf->keepalive_time)
snmp_varbind_int(c, 0);
else
snmp_varbind_int(c,
(bgp_conn) ? bgp_conn->keepalive_time : 0);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_min_as_org_interval(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
/* value should be in 1..65535 but is not supported by bird */
snmp_varbind_int(c, 0);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_route_adv_interval(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
/* value should be in 1..65535 but is not supported by bird */
snmp_varbind_int(c, 0);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_in_update_elapsed_time(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
enum snmp_search_res res;
DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
if (res != SNMP_SEARCH_OK)
return res;
snmp_varbind_gauge32(c,
(current_time() - bgp_proto->last_rx_update) TO_S
);
return SNMP_SEARCH_OK;
}
static enum snmp_search_res
fill_local_id(struct mib_walk_state *walk UNUSED, struct snmp_pdu *c)
{
if (c->sr_vb_start->name.n_subid != 4)
return SNMP_SEARCH_NO_INSTANCE;
ip4_addr router_id_ip = ip4_from_u32(c->p->bgp4_local_id);
snmp_varbind_ip4(c, router_id_ip);
return SNMP_SEARCH_OK;
}
/*
* bgp4_next_peer - find next BGP peer with IPv4 address
* @state: MIB tree walk state
* @c: SNMP PDU context data
*
* Update TX buffer VarBind name to next peer address.
*/
static int
bgp4_next_peer(struct mib_walk_state *state, struct snmp_pdu *c)
{
struct oid *oid = &c->sr_vb_start->name;
/* BGP4-MIB::bgpPeerIdentifier */
STATIC_OID(9) bgp4_peer_id = STATIC_OID_INITIALIZER(9, SNMP_MGMT,
/* ids */ SNMP_MIB_2, SNMP_BGP4_MIB,
BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER);
ip4_addr ip4 = ip4_from_oid(oid);
const struct oid *peer_oid = (const struct oid *) &bgp4_peer_id;
int precise = 1;
if (oid->n_subid > 9)
precise = 0;
if (oid->n_subid != 9 || snmp_oid_compare(oid, peer_oid) < 0)
{
int old = snmp_oid_size(oid);
int new = snmp_oid_size(peer_oid);
if (new - old > 0 && snmp_tbuf_reserve(c, new - old))
oid = &c->sr_vb_start->name;
c->buffer += (new - old);
snmp_oid_copy(oid, peer_oid);
oid->include = 1;
}
ASSUME(oid->n_subid == 9);
/* Stack has one more node for empty prefix (tree root) */
ASSUME(state->stack_pos > 10);
oid->ids[4] = state->stack[10]->empty.id;
net_addr net;
net_fill_ip4(&net, ip4, IP4_MAX_PREFIX_LENGTH);
struct f_trie_walk_state ws;
int match = trie_walk_init(&ws, c->p->bgp_trie, &net, 1);
if (match && oid->include && precise)
{
oid->include = 0;
ip4_to_oid(oid, ip4);
return 0;
}
/* We skip the first match as we should not include ip address in oid */
if (match)
(void) trie_walk_next(&ws, &net);
if (trie_walk_next(&ws, &net))
{
ASSUME(oid->n_subid == 9);
ip4_addr res = ipa_to_ip4(net_prefix(&net));
ip4_to_oid(oid, res);
return 0;
}
return 1;
}
/*
* snmp_bgp4_show_info - display info BGP4-MIB
* @p: SNMP protocol instance
*
* Print info about BGP4-MIB status and bound bgp peers to cli.
*/
void
snmp_bgp4_show_info(struct snmp_proto *p)
{
/* TODO: Use special code (not -1006) for printing MIB, or BGP4-MIB data?
* don't forget add it into doc/reply_code */
cli_msg(-1006, " BGP4-MIB");
cli_msg(-1006, " Local AS %u", p->bgp4_local_as);
cli_msg(-1006, " Local router id %R", p->bgp4_local_id);
cli_msg(-1006, " BGP peers");
if (p->bgp_hash.count == 0 || !snmp_is_active(p))
{
cli_msg(-1006, " <no peers available>");
}
if (!snmp_is_active(p))
return;
HASH_WALK(p->bgp_hash, next, peer)
{
cli_msg(-1006, " Protocol name: %s", peer->bgp_proto->p.name);
cli_msg(-1006, " Remote IPv4 address: %I4", peer->peer_ip);
cli_msg(-1006, " Remote router id %R", peer->bgp_proto->remote_id);
/* TODO: add peer connection local ip */
}
HASH_WALK_END;
}
void
snmp_bgp4_register(struct snmp_proto *p)
{
/* Register the whole BGP4-MIB::bgp root tree node */
struct snmp_registration *reg;
reg = snmp_registration_create(p, BGP4_MIB_ID);
struct oid *oid = mb_allocz(p->pool, sizeof(bgp4_mib_oid));
memcpy(oid, &bgp4_mib_oid, sizeof(bgp4_mib_oid));
reg->reg_hook_ok = NULL;
reg->reg_hook_fail = snmp_bgp_reg_failed;
/*
* We set both upper bound and index to zero, therefore only single OID
* is being registered.
*/
snmp_register(p, oid, 0, 0, SNMP_REGISTER_TREE);
}
/*
* snmp_bgp4_start - prepare BGP4-MIB
* @p: SNMP protocol instance holding memory pool
* @with_mib_tree: flag choosing to insert BGP4-MIB into MIB tree
*
* This function create all runtime bindings to BGP procotol structures.
* It is gruaranteed that the BGP protocols exist.
*/
void
snmp_bgp4_start(struct snmp_proto *p, int with_mib_tree)
{
struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
/* Create binding to BGP protocols */
struct snmp_bond *b;
WALK_LIST(b, cf->bgp_entries)
{
const struct bgp_config *bgp_config = (struct bgp_config *) b->config;
const struct bgp_proto *bgp = SKIP_BACK(struct bgp_proto, p,
bgp_config->c.proto);
struct snmp_bgp_peer *peer = \
mb_alloc(p->pool, sizeof(struct snmp_bgp_peer));
peer->bgp_proto = bgp;
peer->peer_ip = ipa_to_ip4(bgp->remote_ip);
struct net_addr net;
net_fill_ip4(&net, ipa_to_ip4(bgp->remote_ip), IP4_MAX_PREFIX_LENGTH);
trie_add_prefix(p->bgp_trie, &net, IP4_MAX_PREFIX_LENGTH,
IP4_MAX_PREFIX_LENGTH);
snmp_hash_add_peer(p, peer);
}
if (!with_mib_tree)
return;
const STATIC_OID(4) bgp4_mib_peer_entry = STATIC_OID_INITIALIZER(4, SNMP_MGMT,
/* ids */ SNMP_MIB_2, SNMP_BGP4_MIB, BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY);
(void) mib_tree_hint(p->pool, p->mib_tree,
(const struct oid *) &bgp4_mib_oid, BGP4_MIB_IDENTIFIER);
(void) mib_tree_hint(p->pool, p->mib_tree,
(const struct oid *) &bgp4_mib_peer_entry, BGP4_MIB_IN_UPDATE_ELAPSED_TIME);
mib_node_u *node;
struct mib_leaf *leaf;
STATIC_OID(4) bgp4_var = STATIC_OID_INITIALIZER(4, SNMP_MGMT,
/* ids */ SNMP_MIB_2, SNMP_BGP4_MIB, BGP4_MIB_VERSION, 0);
struct {
u32 id;
enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_pdu *c);
enum agentx_type type;
int size;
} leafs[] = {
{
.id = BGP4_MIB_VERSION,
.filler = fill_bgp_version,
.type = AGENTX_OCTET_STRING,
.size = snmp_str_size_from_len(sizeof(BGP4_VERSIONS)),
},
{
.id = BGP4_MIB_LOCAL_AS,
.filler = fill_local_as,
.type = AGENTX_INTEGER,
},
{
.id = BGP4_MIB_IDENTIFIER,
.filler = fill_local_id,
.type = AGENTX_IP_ADDRESS,
},
};
for (uint i = 0; i < ARRAY_SIZE(leafs); i++)
{
bgp4_var.ids[ARRAY_SIZE(bgp4_var.ids) - 2] = leafs[i].id;
node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_var, 1);
ASSUME(mib_node_is_leaf(node));
leaf = &node->leaf;
leaf->filler = leafs[i].filler;
leaf->call_next = NULL;
leaf->type = leafs[i].type;
leaf->size = leafs[i].size;
}
STATIC_OID(5) bgp4_entry_var = STATIC_OID_INITIALIZER(5, SNMP_MGMT,
/* ids */ SNMP_MIB_2, SNMP_BGP4_MIB,
BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER);
struct {
enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_pdu *c);
enum agentx_type type;
int size;
} entry_leafs[] = {
[BGP4_MIB_PEER_IDENTIFIER] = {
.filler = fill_peer_id,
.type = AGENTX_IP_ADDRESS,
},
[BGP4_MIB_STATE] = {
.filler = fill_peer_state,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_ADMIN_STATUS] = {
.filler = fill_admin_status,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_NEGOTIATED_VERSION] = {
.filler = fill_neg_version,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_LOCAL_ADDR] = {
.filler = fill_local_addr,
.type = AGENTX_IP_ADDRESS,
},
[BGP4_MIB_LOCAL_PORT] = {
.filler = fill_local_port,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_REMOTE_ADDR] = {
.filler = fill_remote_addr,
.type = AGENTX_IP_ADDRESS,
},
[BGP4_MIB_REMOTE_PORT] = {
.filler = fill_remote_port,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_REMOTE_AS] = {
.filler = fill_remote_as,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_RX_UPDATES] = {
.filler = fill_in_updates,
.type = AGENTX_COUNTER_32,
},
[BGP4_MIB_TX_UPDATES] = {
.filler = fill_out_updates,
.type = AGENTX_COUNTER_32,
},
[BGP4_MIB_RX_MESSAGES] = {
.filler = fill_in_total_msg,
.type = AGENTX_COUNTER_32,
},
[BGP4_MIB_TX_MESSAGES] = {
.filler = fill_out_total_msg,
.type = AGENTX_COUNTER_32,
},
[BGP4_MIB_LAST_ERROR] = {
.filler = fill_last_err,
.type = AGENTX_OCTET_STRING,
.size = snmp_str_size_from_len(2),
},
[BGP4_MIB_FSM_TRANSITIONS] = {
.filler = fill_established_trans,
.type = AGENTX_COUNTER_32,
},
[BGP4_MIB_FSM_ESTABLISHED_TIME] = {
.filler = fill_established_time,
.type = AGENTX_GAUGE_32,
},
[BGP4_MIB_RETRY_INTERVAL] = {
.filler = fill_retry_interval,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_HOLD_TIME] = {
.filler = fill_hold_time,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_KEEPALIVE] = {
.filler = fill_keep_alive,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_HOLD_TIME_CONFIGURED] = {
.filler = fill_hold_time_conf,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_KEEPALIVE_CONFIGURED] = {
.filler = fill_keep_alive_conf,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_ORIGINATION_INTERVAL] = {
.filler = fill_min_as_org_interval,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_MIN_ROUTE_ADVERTISEMENT] = {
.filler = fill_route_adv_interval,
.type = AGENTX_INTEGER,
},
[BGP4_MIB_IN_UPDATE_ELAPSED_TIME] = {
.filler = fill_in_update_elapsed_time,
.type = AGENTX_GAUGE_32,
},
}; /* struct _anon entry_leafs[] */
for (enum bgp4_mib_peer_entry_row e = BGP4_MIB_PEER_IDENTIFIER;
e <= BGP4_MIB_IN_UPDATE_ELAPSED_TIME; e++)
{
bgp4_entry_var.ids[ARRAY_SIZE(bgp4_entry_var.ids) - 1] = (u32) e;
node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_entry_var, 1);
ASSUME(mib_node_is_leaf(node));
leaf = &node->leaf;
leaf->filler = entry_leafs[e].filler;
leaf->call_next = bgp4_next_peer;
leaf->type = entry_leafs[e].type;
leaf->size = entry_leafs[e].size;
}
}