0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

SNMP: Major leap forward

Solved segmentation faults.
This commit is contained in:
Vojtech Vilimek 2024-01-24 00:23:31 +01:00
parent e8cad1f275
commit c7ca8b66c1
9 changed files with 871 additions and 1111 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,39 +4,41 @@
#include "snmp.h"
#include "subagent.h"
#define BGP4_MIB 15
/* peers attributes */
enum BGP4_MIB_PEER_TABLE {
SNMP_BGP_PEER_IDENTIFIER = 1,
SNMP_BGP_STATE = 2,
SNMP_BGP_ADMIN_STATUS = 3, /* in read-only mode */
SNMP_BGP_NEGOTIATED_VERSION = 4,
SNMP_BGP_LOCAL_ADDR = 5,
SNMP_BGP_LOCAL_PORT = 6,
SNMP_BGP_REMOTE_ADDR = 7,
SNMP_BGP_REMOTE_PORT = 8,
SNMP_BGP_REMOTE_AS = 9,
SNMP_BGP_RX_UPDATES = 10, /* in updates */
SNMP_BGP_TX_UPDATES = 11, /* out updates */
SNMP_BGP_RX_MESSAGES = 12, /* in total messages */
SNMP_BGP_TX_MESSAGES = 13, /* out total messages */
SNMP_BGP_LAST_ERROR = 14,
SNMP_BGP_FSM_TRANSITIONS = 15, /* FSM established transitions */
SNMP_BGP_FSM_ESTABLISHED_TIME = 16,
SNMP_BGP_RETRY_INTERVAL = 17,
SNMP_BGP_HOLD_TIME = 18,
SNMP_BGP_KEEPALIVE = 19,
SNMP_BGP_HOLD_TIME_CONFIGURED = 20,
SNMP_BGP_KEEPALIVE_CONFIGURED = 21,
SNMP_BGP_ORIGINATION_INTERVAL = 22, /* UNSUPPORTED - 0 */
SNMP_BGP_MIN_ROUTE_ADVERTISEMENT = 23, /* UNSUPPORTED - 0 */
SNMP_BGP_IN_UPDATE_ELAPSED_TIME = 24,
enum bgp4_mib_peer_entry_row {
BGP4_MIB_PEER_IDENTIFIER = 1,
BGP4_MIB_STATE = 2,
BGP4_MIB_ADMIN_STATUS = 3, /* in read-only mode */
BGP4_MIB_NEGOTIATED_VERSION = 4,
BGP4_MIB_LOCAL_ADDR = 5,
BGP4_MIB_LOCAL_PORT = 6,
BGP4_MIB_REMOTE_ADDR = 7,
BGP4_MIB_REMOTE_PORT = 8,
BGP4_MIB_REMOTE_AS = 9,
BGP4_MIB_RX_UPDATES = 10, /* in updates */
BGP4_MIB_TX_UPDATES = 11, /* out updates */
BGP4_MIB_RX_MESSAGES = 12, /* in total messages */
BGP4_MIB_TX_MESSAGES = 13, /* out total messages */
BGP4_MIB_LAST_ERROR = 14,
BGP4_MIB_FSM_TRANSITIONS = 15, /* FSM established transitions */
BGP4_MIB_FSM_ESTABLISHED_TIME = 16,
BGP4_MIB_RETRY_INTERVAL = 17,
BGP4_MIB_HOLD_TIME = 18,
BGP4_MIB_KEEPALIVE = 19,
BGP4_MIB_HOLD_TIME_CONFIGURED = 20,
BGP4_MIB_KEEPALIVE_CONFIGURED = 21,
BGP4_MIB_ORIGINATION_INTERVAL = 22, /* UNSUPPORTED - 0 */
BGP4_MIB_MIN_ROUTE_ADVERTISEMENT = 23, /* UNSUPPORTED - 0 */
BGP4_MIB_IN_UPDATE_ELAPSED_TIME = 24,
} PACKED;
/* version of BGP, here BGP-4 */
#define BGP4_VERSIONS ((char[]) { 0x10 }) /* OID bgp.bgpVersion */
/* for OID bgp.bgpPeerTable.bgpPeerEntry.bgpPeerNegotiatedVersion */
#define SNMP_BGP_NEGOTIATED_VER_VALUE 4
#define SNMP_BGP_NEGOTIATED_VER_NO_VALUE 0
#define BGP4_MIB_NEGOTIATED_VER_VALUE 4
#define BGP4_MIB_NEGOTIATED_VER_NO_VALUE 0
void snmp_bgp_register(struct snmp_proto *p);
@ -46,66 +48,81 @@ void snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response *r, struct
u8 snmp_bgp_get_valid(u8 state);
u8 snmp_bgp_getnext_valid(u8 state);
struct oid *snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid);
enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu *c);
//int snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void* tr, struct oid *oid, uint pkt_type);
void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp);
void snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp);
#define SNMP_BGP_VERSION 1
#define SNMP_BGP_LOCAL_AS 2
#define SNMP_BGP_PEER_TABLE 3
#define SNMP_BGP_PEER_ENTRY 1
#define SNMP_BGP_IDENTIFIER 4 /* BGP4-MIB::bgpIdentifier local router id */
/* BGP linearized state */
enum BGP_INTERNAL_STATES {
BGP_INTERNAL_INVALID = 0,
BGP_INTERNAL_START = 1,
BGP_INTERNAL_BGP,
BGP_INTERNAL_VERSION,
BGP_INTERNAL_LOCAL_AS,
BGP_INTERNAL_PEER_TABLE,
BGP_INTERNAL_PEER_ENTRY,
BGP_INTERNAL_PEER_IDENTIFIER,
BGP_INTERNAL_STATE,
BGP_INTERNAL_ADMIN_STATUS,
BGP_INTERNAL_NEGOTIATED_VERSION,
BGP_INTERNAL_LOCAL_ADDR,
BGP_INTERNAL_LOCAL_PORT,
BGP_INTERNAL_REMOTE_ADDR,
BGP_INTERNAL_REMOTE_PORT,
BGP_INTERNAL_REMOTE_AS,
BGP_INTERNAL_RX_UPDATES,
BGP_INTERNAL_TX_UPDATES,
BGP_INTERNAL_RX_MESSAGES,
BGP_INTERNAL_TX_MESSAGES,
BGP_INTERNAL_LAST_ERROR,
BGP_INTERNAL_FSM_TRANSITIONS,
BGP_INTERNAL_FSM_ESTABLISHED_TIME,
BGP_INTERNAL_RETRY_INTERVAL,
BGP_INTERNAL_HOLD_TIME,
BGP_INTERNAL_KEEPALIVE,
BGP_INTERNAL_HOLD_TIME_CONFIGURED,
BGP_INTERNAL_KEEPALIVE_CONFIGURED,
BGP_INTERNAL_ORIGINATION_INTERVAL,
BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT,
BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME,
BGP_INTERNAL_PEER_TABLE_END,
BGP_INTERNAL_IDENTIFIER, /* local identification */
BGP_INTERNAL_END,
BGP_INTERNAL_NO_VALUE = 255,
} PACKED;
enum bgp4_mib_bgp_states {
BGP_MIB_IDLE = 1,
BGP_MIB_CONNECT = 2,
BGP_MIB_ACTIVE = 3,
BGP_MIB_OPENSENT = 4,
BGP_MIB_OPENCONFIRM = 5,
BGP_MIB_ESTABLISHED = 6,
enum bgp4_mib_rows {
BGP4_MIB_VERSION = 1,
BGP4_MIB_LOCAL_AS = 2,
BGP4_MIB_PEER_TABLE = 3, /* subtable */
BGP4_MIB_IDENTIFIER = 4, /* BGP4-MIB::bgpIdentifier local router id */
};
enum bgp4_mib_peer_table_rows {
BGP4_MIB_PEER_ENTRY = 1,
};
enum bgp4_mib_linearized_states {
BGP4_MIB_S_INVALID = 0, /* state invalid */
BGP4_MIB_S_START = 1,
BGP4_MIB_S_BGP,
BGP4_MIB_S_VERSION,
BGP4_MIB_S_LOCAL_AS,
BGP4_MIB_S_PEER_TABLE,
BGP4_MIB_S_PEER_ENTRY,
BGP4_MIB_S_PEER_IDENTIFIER,
BGP4_MIB_S_STATE,
BGP4_MIB_S_ADMIN_STATUS,
BGP4_MIB_S_NEGOTIATED_VERSION,
BGP4_MIB_S_LOCAL_ADDR,
BGP4_MIB_S_LOCAL_PORT,
BGP4_MIB_S_REMOTE_ADDR,
BGP4_MIB_S_REMOTE_PORT,
BGP4_MIB_S_REMOTE_AS,
BGP4_MIB_S_RX_UPDATES,
BGP4_MIB_S_TX_UPDATES,
BGP4_MIB_S_RX_MESSAGES,
BGP4_MIB_S_TX_MESSAGES,
BGP4_MIB_S_LAST_ERROR,
BGP4_MIB_S_FSM_TRANSITIONS,
BGP4_MIB_S_FSM_ESTABLISHED_TIME,
BGP4_MIB_S_RETRY_INTERVAL,
BGP4_MIB_S_HOLD_TIME,
BGP4_MIB_S_KEEPALIVE,
BGP4_MIB_S_HOLD_TIME_CONFIGURED,
BGP4_MIB_S_KEEPALIVE_CONFIGURED,
BGP4_MIB_S_ORIGINATION_INTERVAL,
BGP4_MIB_S_MIN_ROUTE_ADVERTISEMENT,
BGP4_MIB_S_IN_UPDATE_ELAPSED_TIME,
BGP4_MIB_S_PEER_TABLE_END,
BGP4_MIB_S_IDENTIFIER, /* state local identification */
BGP4_MIB_S_END,
BGP4_MIB_S_NO_VALUE = 255,
} PACKED;
/* valid values for BGP4_MIB_STATE */
enum bgp4_mib_bgp_states {
BGP4_MIB_IDLE = 1,
BGP4_MIB_CONNECT = 2,
BGP4_MIB_ACTIVE = 3,
BGP4_MIB_OPENSENT = 4,
BGP4_MIB_OPENCONFIRM = 5,
BGP4_MIB_ESTABLISHED = 6,
};
STATIC_ASSERT(BGP4_MIB_IDLE == BS_IDLE + 1);
STATIC_ASSERT(BGP4_MIB_CONNECT == BS_CONNECT + 1);
STATIC_ASSERT(BGP4_MIB_ACTIVE == BS_ACTIVE + 1);
STATIC_ASSERT(BGP4_MIB_OPENSENT == BS_OPENSENT + 1);
STATIC_ASSERT(BGP4_MIB_OPENCONFIRM == BS_OPENCONFIRM + 1);
STATIC_ASSERT(BGP4_MIB_ESTABLISHED == BS_ESTABLISHED + 1);
/* Traps OID sub-identifiers */
#define BGP4_MIB_ESTABLISHED_NOTIFICATION 1
#define BGP4_MIB_BACKWARD_TRANS_NOTIFICATION 2
#endif

View File

@ -1,5 +1,4 @@
/*
* BIRD -- Simple Network Management Protocol (SNMP) *
/** BIRD -- Simple Network Management Protocol (SNMP) *
* (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
* (c) 2022 CZ.NIC z.s.p.o.
*
@ -134,7 +133,6 @@ static void snmp_cleanup(struct snmp_proto *p);
static int
snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
{
log(L_INFO "snmp_rx_skip with size %u", size);
return 1;
}
@ -144,29 +142,38 @@ snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
*
* The socket tx_hook is called when the TX-buffer is empty, i.e. all data was
* send. This function is used only when we found malformed PDU and we are
* resetting the established session. If called we direcly reset the session.
* resetting the established session. If called, we are reseting the session.
*/
static void
snmp_tx_skip(sock *sk)
{
log(L_INFO "snmp_tx_skip()");
struct snmp_proto *p = sk->data;
snmp_set_state(p, SNMP_DOWN);
proto_notify_state(&p->p, snmp_set_state(p, SNMP_DOWN));
}
/*
* snmp_set_state - change state with associated actions
* @p - SNMP protocol instance
* @state - new SNMP protocol state
*
* This function does not notify the bird about protocol state. It is therefore
* a responsibility of the caller to use the returned value appropriately.
*
* Return current protocol state.
*/
void
int
snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
{
enum snmp_proto_state last = p->state;
TRACE(D_EVENTS, "SNMP changing state to %u", state);
p->state = state;
if (state == SNMP_DOWN && (last == SNMP_REGISTER || last == SNMP_CONN))
{
/* We have a connection established (at least send out agentx-Open-PDU) */
state = SNMP_STOP;
}
/* else - We did not send any packet, we perform protocol cleanup only. */
if (last == SNMP_RESET)
{
@ -174,10 +181,12 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
p->sock = NULL;
}
p->state = state;
switch (state)
{
case SNMP_INIT:
debug("snmp -> SNMP_INIT\n");
DBG("snmp -> SNMP_INIT\n");
ASSERT(last == SNMP_DOWN);
struct object_lock *lock;
lock = p->lock = olock_new(p->pool);
@ -192,12 +201,11 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
lock->hook = snmp_start_locked;
lock->data = p;
olock_acquire(lock);
break;
return PS_START;
case SNMP_LOCKED:
debug("snmp -> SNMP_LOCKED\n");
DBG("snmp -> SNMP_LOCKED\n");
ASSERT(last == SNMP_INIT || SNMP_RESET);
snmp_unset_header(p);
sock *s = sk_new(p->pool);
s->type = SK_TCP_ACTIVE;
s->saddr = ipa_from_ip4(p->local_ip);
@ -220,60 +228,56 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
p->sock = NULL;
tm_start(p->startup_timer, p->timeout);
}
break;
return PS_START;
case SNMP_OPEN:
debug("snmp -> SNMP_OPEN\n");
DBG("snmp -> SNMP_OPEN\n");
ASSERT(last == SNMP_LOCKED);
p->sock->rx_hook = snmp_rx;
p->sock->tx_hook = NULL;
log(L_WARN " Staring subagent %u %u %u %u", p->header_offset, p->last_index, p->last_size, p->last_pkt_id);
snmp_start_subagent(p);
// handle no response (for long time)
break;
return PS_START;
case SNMP_REGISTER:
debug("snmp -> SNMP_REGISTER\n");
DBG("snmp -> SNMP_REGISTER\n");
ASSERT(last == SNMP_OPEN);
snmp_register_mibs(p);
break;
return PS_START;
case SNMP_CONN:
debug("snmp -> SNMP_CONN\n");
DBG("snmp -> SNMP_CONN\n");
ASSERT(last == SNMP_REGISTER);
proto_notify_state(&p->p, PS_UP);
break;
return PS_UP;
case SNMP_STOP:
debug("snmp -> SNMP_STOP\n");
DBG("snmp -> SNMP_STOP\n");
ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
snmp_stop_subagent(p);
p->sock->rx_hook = snmp_rx_skip;
p->sock->tx_hook = snmp_tx_skip;
p->startup_timer->hook = snmp_stop_timeout;
tm_start(p->startup_timer, p->timeout);
proto_notify_state(&p->p, PS_STOP);
break;
return PS_STOP;
case SNMP_DOWN:
debug("snmp -> SNMP_DOWN\n");
//ASSUME(last == SNMP_STOP || SNMP_INIT || SNMP_LOCKED || SNMP_OPEN);
DBG("snmp -> SNMP_DOWN\n");
snmp_cleanup(p);
// FIXME: handle the state in which we call proto_notify_state and
// immediately return PS_DOWN from snmp_shutdown()
proto_notify_state(&p->p, PS_DOWN);
break;
return PS_DOWN;
case SNMP_RESET:
debug("snmp -> SNMP_RESET\n");
DBG("snmp -> SNMP_RESET\n");
ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
ASSUME(p->sock);
p->sock->rx_hook = snmp_rx_skip;
p->sock->tx_hook = snmp_tx_skip;
break;
return PS_STOP;
default:
die("unknown state transition");
die("unknown snmp state transition");
return PS_DOWN;
}
}
@ -281,7 +285,7 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
* snmp_init - preinitialize SNMP instance
* @CF - SNMP configuration generic handle
*
* Return value is generic handle pointing to preinitialized SNMP procotol
* Returns a generic handle pointing to preinitialized SNMP procotol
* instance.
*/
static struct proto *
@ -344,24 +348,8 @@ snmp_cleanup(struct snmp_proto *p)
p->bgp_trie = NULL;
}
#if 0
/*
* snmp_down - stop the SNMP protocol and free resources
* @p - SNMP protocol instance
*
* AgentX session is destroyed by closing underlying socket and all resources
* are freed. Afterwards, the PS_DOWN protocol state is announced.
*/
void
snmp_down(struct snmp_proto *p)
{
snmp_cleanup(p);
proto_notify_state(&p->p, PS_DOWN);
}
#endif
/*
* snmp_connected - start AgentX session on established channel
* snmp_connected - start AgentX session on created socket
* @sk - socket owned by SNMP protocol instance
*
* Starts the AgentX communication by sending an agentx-Open-PDU.
@ -375,32 +363,18 @@ snmp_connected(sock *sk)
}
/*
* snmp_sock_disconnect - end or reset socket connection
* snmp_reset - end the communication on AgentX session
* @p - SNMP protocol instance
*
* If the @reconnect flags is set, we close the socket and then reestablish
* the AgentX session by reentering the start procedure as from the
* snmp_start_locked() function.
* Otherwise we simply shutdown the SNMP protocol if the flag is clear.
* End the communication on AgentX session by downing the whole procotol. This
* causes socket closure that implies AgentX session disconnection.
* This function is internal and shouldn't be used outside the SNMP module.
*/
void
snmp_sock_disconnect(struct snmp_proto *p, int reconnect)
snmp_reset(struct snmp_proto *p)
{
tm_stop(p->ping_timer);
if (!reconnect)
{
snmp_set_state(p, SNMP_DOWN);
return;
}
// TODO - wouldn't be better to use inter jump state RESET (soft reset) ?
snmp_set_state(p, SNMP_DOWN);
/* We try to reconnect after a short delay */
//p->startup_timer->hook = snmp_startup_timeout;
//tm_start(p->startup_timer, 4); // TODO make me configurable
proto_notify_state(&p->p, snmp_set_state(p, SNMP_DOWN));
}
/*
@ -414,7 +388,7 @@ snmp_sock_err(sock *sk, int UNUSED err)
struct snmp_proto *p = sk->data;
TRACE(D_EVENTS, "SNMP socket error %d", err);
snmp_sock_disconnect(p, 1);
snmp_reset(p);
}
/*
@ -444,80 +418,17 @@ snmp_start_locked(struct object_lock *lock)
* snmp_reconnect - helper restarting the AgentX session on packet errors
* @tm - the startup_timer holding the SNMP protocol instance
*
* Rerun the SNMP module start procedure. Used in situations when the master
* agent returns an agentx-Response-PDU with 'Not Opened' error. We do not close
* the socket if have one.
* Try to recover from an error by reseting the SNMP protocol. It is a simple
* snmp_reset() wrapper for timers.
*/
void
snmp_reconnect(timer *tm)
{
struct snmp_proto *p = tm->data;
// TODO
snmp_set_state(p, SNMP_DOWN);
snmp_reset(p);
return;
/* reset the connection */
snmp_sock_disconnect(p, 1);
// TODO better snmp_set_state(SNMP_DOWM); ??
if (p->state == SNMP_STOP ||
p->state == SNMP_DOWN)
return;
// TO-DO is SNMP_RESET really needed ?
if (p->state == SNMP_INIT ||
p->state == SNMP_RESET)
//snmp_startup(p);
{}
if (!p->sock)
snmp_start_locked(p->lock);
else
snmp_connected(p->sock);
}
#if 0
/*
* snmp_startup - start initialized SNMP protocol
* @p - SNMP protocol to start
*
* Starting of SNMP protocols begins with address acqusition through object
* lock. Next step is handled by snmp_start_locked() function.
* This function is internal and shouldn't be used outside the SNMP
* module.
*/
void
snmp_startup(struct snmp_proto *p)
{
// TODO inline to snmp_start()
if (p->state == SNMP_OPEN ||
p->state == SNMP_REGISTER ||
p->state == SNMP_CONN)
{
return;
}
if (p->lock)
{
snmp_start_locked(p->lock);
return;
}
p->state = SNMP_INIT;
struct object_lock *lock;
lock = p->lock = olock_new(p->pool);
// lock->addr
// lock->port
// lock->iface
// lock->vrf
lock->type = OBJLOCK_TCP;
lock->hook = snmp_start_locked;
lock->data = p;
olock_acquire(lock);
}
#endif
/*
* snmp_startup_timeout - start the initiliazed SNMP protocol
* @tm - the startup_timer holding the SNMP protocol instance.
@ -547,14 +458,15 @@ static void
snmp_stop_timeout(timer *tm)
{
struct snmp_proto *p = tm->data;
snmp_set_state(p, SNMP_DOWN);
proto_notify_state(&p->p, snmp_set_state(p, SNMP_DOWN));
}
/*
* snmp_ping_timeout - send a agentx-Ping-PDU
* @tm - the ping_timer holding the SNMP protocol instance.
*
* Send an agentx-Ping-PDU and reset the timer for next ping.
* Send an agentx-Ping-PDU. This function is periodically called by ping
* timer.
*/
static void
snmp_ping_timeout(timer *tm)
@ -568,7 +480,7 @@ snmp_ping_timeout(timer *tm)
* @P - SNMP protocol generic handle
*
* The first step in AgentX subagent startup is protocol initialition.
* We must prepare lists, find BGP peers and finally asynchornously open
* We must prepare lists, find BGP peers and finally asynchronously open
* a AgentX subagent session through snmp_startup() function call.
*/
static int
@ -584,7 +496,7 @@ snmp_start(struct proto *P)
p->bgp_local_as = cf->bgp_local_as;
p->bgp_local_id = cf->bgp_local_id;
p->timeout = cf->timeout;
// add default value for startup_delay inside bison .Y file
// TODO add default value for startup_delay inside bison .Y file
p->startup_delay = cf->startup_delay;
p->pool = p->p.pool;
@ -602,11 +514,7 @@ snmp_start(struct proto *P)
snmp_bgp_start(p);
snmp_unset_header(p);
snmp_set_state(p, SNMP_INIT);
return PS_START;
return snmp_set_state(p, SNMP_INIT);
}
static inline int
@ -647,23 +555,23 @@ skip:
* @CF - SNMP protocol configuration generic handle carring new values
*
* We accept the reconfiguration if the new configuration @CF is identical with
* the currently deployed. Otherwise we deny reconfiguration because
* the currently deployed configuration. Otherwise we deny reconfiguration because
* the implementation would be cumbersome.
*/
static int
snmp_reconfigure(struct proto *P, struct proto_config *CF)
{
// remove lost bonds, add newly created
struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
/* We are searching for configuration changes */
int retval = snmp_reconfigure_logic(p, new);
if (retval) {
if (retval)
{
/* Reinitialize the hash after snmp_shutdown() */
HASH_INIT(p->bgp_hash, p->pool, 10);
snmp_bgp_start(p);
snmp_unset_header(p);
}
return retval;
@ -683,7 +591,6 @@ snmp_show_proto_info(struct proto *P)
// TODO move me into the bgp_mib.c
cli_msg(-1006, " BGP4-MIB");
// TODO enabled
cli_msg(-1006, " Local AS %u", p->bgp_local_as);
cli_msg(-1006, " Local router id %R", p->bgp_local_id);
cli_msg(-1006, " BGP peers");
@ -726,20 +633,7 @@ static int
snmp_shutdown(struct proto *P)
{
struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
if (p->state == SNMP_REGISTER ||
p->state == SNMP_CONN)
{
/* We have a connection established (at leased send out Open-PDU). */
snmp_set_state(p, SNMP_STOP);
return PS_STOP;
}
else
{
/* We did not create a connection, we clean the lock and other stuff. */
snmp_set_state(p, SNMP_DOWN);
return PS_DOWN;
}
return snmp_set_state(p, SNMP_DOWN);
}

View File

@ -29,6 +29,7 @@
#define SNMP_RX_BUFFER_SIZE 8192
#define SNMP_TX_BUFFER_SIZE 8192
#define SNMP_PKT_SIZE_MAX 8192
enum snmp_proto_state {
SNMP_DOWN = 0,
@ -60,11 +61,15 @@ struct snmp_config {
btime timeout;
btime startup_delay;
u8 priority;
//struct iface *iface;
//struct iface *iface; TODO
u32 bonds;
const char *description;
list bgp_entries;
// TODO add support for subagent oid identification
const char *description; /* The order of fields is not arbitrary */
list bgp_entries; /* We want dynamically allocated fields to be
* at the end of the config struct.
* We use this fact to check differences of
* nonallocated parts of configs with memcpy
*/
//const struct oid *oid_identifier; TODO
};
#define SNMP_BGP_P_REGISTERING 0x01
@ -109,13 +114,6 @@ struct snmp_proto {
sock *sock;
/* Partial packet processing */
uint header_offset; /* offset of PDU header in TX-buffer during
* partial parsing */
uint last_index; /* stores last index during partial parsing */
uint last_size; /* number of bytes used inside TX-buffer */
uint last_pkt_id; /* stores packetId to use for partial packet */
btime timeout; /* timeout is part of MIB registration. It
specifies how long should the master
@ -147,8 +145,8 @@ void snmp_startup(struct snmp_proto *p);
void snmp_connected(sock *sk);
void snmp_startup_timeout(timer *tm);
void snmp_reconnect(timer *tm);
void snmp_sock_disconnect(struct snmp_proto *p, int reconnect);
void snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state);
int snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state);
void snmp_reset(struct snmp_proto *p);
#endif

View File

@ -11,29 +11,25 @@
#include "snmp_utils.h"
inline void
snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk)
snmp_pdu_context(struct snmp_pdu *pdu, sock *sk)
{
pdu->error = AGENTX_RES_NO_ERROR;
if (!snmp_is_partial(p))
{
pdu->buffer = sk->tpos;
pdu->size = sk->tbuf + sk->tbsize - sk->tpos;
pdu->index = 0;
return;
}
pdu->buffer = sk->tbuf + p->header_offset + p->last_size;
pdu->size = sk->tbuf + sk->tbsize - pdu->buffer;
pdu->index = p->last_index;
pdu->buffer = sk->tpos;
pdu->size = sk->tbuf + sk->tbsize - sk->tpos;
pdu->index = 0;
}
/**
* snmp_session - store packet ids from protocol to header
* @p: source SNMP protocol instance
* @h: dest PDU header
*/
inline void
snmp_session(const struct snmp_proto *p, struct agentx_header *h)
{
STORE_U32(h->session_id, p->session_id);
STORE_U32(h->transaction_id, p->transaction_id);
STORE_U32(h->packet_id, p->packet_id);
//log(L_INFO "storing packet id %u into the header %p", p->packet_id, h);
}
inline int
@ -42,17 +38,6 @@ snmp_has_context(const struct agentx_header *h)
return h->flags & AGENTX_NON_DEFAULT_CONTEXT;
}
inline byte *
snmp_add_context(struct snmp_proto *p, struct agentx_header *h, uint contid)
{
u8 flags = LOAD_U8(h->flags);
STORE_U8(h->flags, flags | AGENTX_NON_DEFAULT_CONTEXT);
// TODO append the context after the header
(void)p;
(void)contid;
return (void *)h + AGENTX_HEADER_SIZE;
}
inline void *
snmp_varbind_data(const struct agentx_varbind *vb)
{
@ -64,11 +49,12 @@ snmp_varbind_data(const struct agentx_varbind *vb)
* snmp_is_oid_empty - check if oid is null-valued
* @oid: object identifier to check
*
* Test if the oid header is full of zeroes. For @oid NULL returns 0.
* Test if the oid header is full of zeroes. For NULL-pointer @oid returns 0.
*/
int
snmp_is_oid_empty(const struct oid *oid)
{
/* We intentionaly ignore padding that should be zeroed */
if (oid != NULL)
return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 &&
LOAD_U8(oid->include) == 0;
@ -96,9 +82,9 @@ void
snmp_oid_copy(struct oid *dest, const struct oid *src)
{
STORE_U8(dest->n_subid, src->n_subid);
STORE_U8(dest->prefix, src->prefix);
STORE_U8(dest->prefix, src->prefix);
STORE_U8(dest->include, src->include ? 1 : 0);
STORE_U8(dest->pad, 0);
STORE_U8(dest->reserved, 0);
for (int i = 0; i < LOAD_U8(src->n_subid); i++)
STORE_U32(dest->ids[i], src->ids[i]);
@ -118,7 +104,7 @@ snmp_oid_duplicate(pool *pool, const struct oid *oid)
}
/**
* create new null oid (blank)
* snmp_oid_blank - create new null oid (blank)
* @p: pool hodling snmp_proto structure
*/
struct oid *
@ -128,10 +114,10 @@ snmp_oid_blank(struct snmp_proto *p)
}
/**
* snmp_str_size_from_len - return in-buffer octet-string size
* snmp_str_size_from_len - return in-buffer octet string size
* @len: length of C-string, returned from strlen()
*/
size_t
inline size_t
snmp_str_size_from_len(uint len)
{
return 4 + BIRD_ALIGN(len, 4);
@ -157,7 +143,7 @@ snmp_str_size(const char *str)
uint
snmp_oid_size(const struct oid *o)
{
return 4 + (o->n_subid * 4);
return 4 + (LOAD_U8(o->n_subid) * 4);
}
/**
@ -165,7 +151,7 @@ snmp_oid_size(const struct oid *o)
* @n_subid: number of ids in oid
*/
inline size_t
snmp_oid_sizeof(uint n_subid)
snmp_oid_size_from_len(uint n_subid)
{
return sizeof(struct oid) + n_subid * sizeof(u32);
}
@ -319,6 +305,32 @@ snmp_varbind_size(const struct agentx_varbind *vb, uint limit)
}
}
/**
* snmp_varbind_size_from_len - get size in-buffer VarBind for known OID and data
* @n_subid: number of subidentifiers of the VarBind's OID name
* @type: type of VarBind
* @len: length of variably long data
*
* For types with fixed size the @len is not used. For types such as Octet
* String, or OID the @len is used directly.
*
* Return number of bytes used by VarBind in specified form.
*/
inline size_t
snmp_varbind_size_from_len(uint n_subid, enum agentx_type type, uint len)
{
size_t sz = snmp_oid_size_from_len(n_subid)
+ sizeof(struct agentx_varbind) - sizeof(struct oid);
int data_sz = agentx_type_size(type);
if (data_sz < 0)
sz += len;
else
sz += data_sz;
return sz;
}
/*
* snmp_test_varbind - test validity of VarBind's type
* @vb: VarBind to test
@ -369,7 +381,7 @@ struct agentx_varbind *
snmp_create_varbind(byte *buf, struct oid *oid)
{
struct agentx_varbind *vb = (void *) buf;
STORE_U16(vb->pad, 0);
STORE_U16(vb->reserved, 0);
snmp_oid_copy(&vb->name, oid);
return vb;
}
@ -391,7 +403,7 @@ snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new)
int
snmp_valid_ip4_index(const struct oid *o, uint start)
{
if (start + 3 < o->n_subid)
if (start + 3 < LOAD_U8(o->n_subid))
return snmp_valid_ip4_index_unsafe(o, start);
else
return 0;
@ -409,7 +421,7 @@ int
snmp_valid_ip4_index_unsafe(const struct oid *o, uint start)
{
for (int i = 0; i < 4; i++)
if (o->ids[start + i] >= 256)
if (LOAD_U32(o->ids[start + i]) >= 256)
return 0;
return 1;
@ -501,6 +513,14 @@ snmp_put_fbyte(byte *buf, u8 data)
return buf + 3;
}
/*
* snmp_oid_ip4_index - OID append IPv4 index
* @o: OID to use
* @start: index of IP addr's MSB
* @addr: IPv4 address to use
*
* The indices from start to (inclusive) start+3 are overwritten by @addr bytes.
*/
void
snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
{
@ -511,37 +531,6 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
STORE_U32(o->ids[start + 3], temp & 0xFF);
}
void UNUSED
snmp_oid_dump(const struct oid *oid)
{
log(L_WARN "OID DUMP ========");
if (oid == NULL)
{
log(L_WARN "is eqaul to NULL");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
else if (snmp_is_oid_empty(oid))
{
log(L_WARN "is empty");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
log(L_WARN " #ids: %4u prefix %3u include: %5s",
oid->n_subid, oid->prefix, (oid->include)? "true" : "false");
log(L_WARN "IDS -------------");
for (int i = 0; i < oid->n_subid; i++)
log(L_WARN " %2u: %11u ~ 0x%08X", i, oid->ids[i], oid->ids[i]);
log(L_WARN "OID DUMP END ====");
log(L_WARN);
}
/** snmp_oid_compare - find the lexicographical order relation between @left and @right
* both @left and @right has to be non-blank.
@ -665,11 +654,13 @@ agentx_type_size(enum agentx_type type)
type == AGENTX_INTEGER)
return 4;
/* AGENTX_COUNTER_64 */
if (type == AGENTX_COUNTER_64)
return 8;
/* AGENTX_OBJECT_ID, AGENTX_OCTET_STRING, AGENTX_IP_ADDRESS, AGENTX_OPAQUE */
if (AGENTX_IP_ADDRESS)
return snmp_str_size_from_len(4);
/* AGENTX_OBJECT_ID, AGENTX_OCTET_STRING, AGENTX_OPAQUE */
else
return -1;
}
@ -759,78 +750,39 @@ snmp_test_close_reason(byte value)
return 0;
}
inline struct agentx_header *
snmp_create_tx_header(struct snmp_proto *p, byte *tbuf)
{
/* the response is created always in TX-buffer */
p->header_offset = tbuf - p->sock->tbuf;
ASSERT(p->header_offset < p->sock->tbsize);
return (struct agentx_header *) tbuf;
}
/*
* Partial header manipulation functions
* Debugging
*/
/*
* snmp_is_partial - check if we have a partially parted packet in TX-buffer
* @p: SNMP protocol instance
*/
inline int
snmp_is_partial(const struct snmp_proto *p)
void UNUSED
snmp_oid_dump(const struct oid *oid)
{
return p->last_size > 0;
}
log(L_WARN "OID DUMP ========");
/*
* snmp_get_header - restore partial packet's header from TX-buffer
* @p: SNMP protocol instance
*/
inline struct agentx_header *
snmp_get_header(const struct snmp_proto *p)
{
/* Nonzero last size indicates existence of partial packet */
ASSERT(p->last_size && p->header_offset < p->sock->tbsize);
return (struct agentx_header *) (p->sock->tbuf + p->header_offset);
}
if (oid == NULL)
{
log(L_WARN "is eqaul to NULL");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
/*
* snmp_set_header - store partial packet's header into protocol
* @p: SNMP protocol instance
* @h: header of the currently parsed PDU
* @c: SNMP PDU context
*
* Store the needed values regarding later partial PDU processing.
*/
inline void
snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c)
{
sock *sk = p->sock;
// TODO agentx_headier in last_size or not?
ASSERT(c->buffer - sk->tpos >= AGENTX_HEADER_SIZE);
p->last_size = c->buffer - sk->tpos + p->last_size;
p->header_offset = (((byte *) h) - sk->tbuf);
p->last_index = c->index;
log(L_INFO "using p->packet_id %u as a p->last_pkt_id %u", p->packet_id, p->last_pkt_id);
p->last_pkt_id = p->packet_id;
log(L_INFO "snmp_set_header() tbuf %p tpos %p buffer %p header %p header2 %p offset %u last_size %u last_index %u last_pkt_id %u",
sk->tbuf, sk->tpos, c->buffer,h,(byte*)h, p->header_offset, p->last_size,
p->last_index, p->last_pkt_id);
}
else if (snmp_is_oid_empty(oid))
{
log(L_WARN "is empty");
log(L_WARN "OID DUMP END ====");
log(L_WARN ".");
return;
}
/*
* snmp_unset_header - clean partial packet's header
* @p: SNMP protocol instance
*
* Clean the partial packet processing fields of protocol when the packet is
* fully processed.
*/
inline void
snmp_unset_header(struct snmp_proto *p)
{
p->last_size = 0;
p->header_offset = 0;
p->last_index = 0;
p->last_pkt_id = 0;
}
log(L_WARN " #ids: %4u prefix %3u include: %5s",
oid->n_subid, oid->prefix, (oid->include)? "true" : "false");
log(L_WARN "IDS -------------");
for (int i = 0; i < oid->n_subid; i++)
log(L_WARN " %2u: %11u ~ 0x%08X", i, oid->ids[i], oid->ids[i]);
log(L_WARN "OID DUMP END ====");
log(L_WARN);
}

View File

@ -4,59 +4,65 @@
#include "subagent.h"
uint snmp_pkt_len(const byte *start, const byte *end);
/*
*
* AgentX Variable Biding (VarBind) utils
*
*/
/*
* AgentX - Variable Binding (VarBind) type utils
*/
void snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t);
enum agentx_type snmp_get_varbind_type(const struct agentx_varbind *vb);
int agentx_type_size(enum agentx_type t);
/* type Octet String */
size_t snmp_str_size_from_len(uint len);
size_t snmp_str_size(const char *str);
/* type OID - Object Identifier */
int snmp_is_oid_empty(const struct oid *oid);
uint snmp_oid_size(const struct oid *o);
size_t snmp_oid_size_from_len(uint n_subid);
void snmp_oid_copy(struct oid *dest, const struct oid *src);
int snmp_oid_compare(const struct oid *first, const struct oid *second);
/* type IPv4 */
int snmp_valid_ip4_index(const struct oid *o, uint start);
int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start);
uint snmp_oid_size(const struct oid *o);
size_t snmp_oid_sizeof(uint n_subid);
void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr);
/*
* AgentX - Variable Binding (VarBind) manupulation
*/
uint snmp_varbind_hdr_size_from_oid(const struct oid *oid);
uint snmp_varbind_header_size(const struct agentx_varbind *vb);
uint snmp_varbind_size(const struct agentx_varbind *vb, uint limit);
uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb);
size_t snmp_varbind_size_from_len(uint n_subid, enum agentx_type t, uint len);
int snmp_test_varbind(const struct agentx_varbind *vb);
void *snmp_varbind_data(const struct agentx_varbind *vb);
/*
* AgentX - PDU headers, types, contexts
*/
void snmp_session(const struct snmp_proto *p, struct agentx_header *h);
int snmp_has_context(const struct agentx_header *h);
void snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk);
void snmp_oid_copy(struct oid *dest, const struct oid *src);
void snmp_pdu_context(struct snmp_pdu *pdu, sock *sk);
struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid);
struct oid *snmp_oid_blank(struct snmp_proto *p);
void *snmp_varbind_data(const struct agentx_varbind *vb);
struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid);
struct agentx_varbind *snmp_create_varbind_null(byte *buf);
byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new);
int snmp_test_close_reason(byte value);
int snmp_oid_compare(const struct oid *first, const struct oid *second);
byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid);
byte *snmp_no_such_instance(byte *buf, struct agentx_varbind *vb, struct oid *oid);
byte *snmp_put_str(byte *buf, const char *str);
byte *snmp_put_nstr(byte *buf, const char *str, uint len);
byte *snmp_put_blank(byte *buf);
byte *snmp_put_oid(byte *buf, struct oid *oid);
byte *snmp_put_ip4(byte *buf, ip4_addr ip4);
byte *snmp_put_fbyte(byte *buf, u8 data);
void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr);
void snmp_oid_dump(const struct oid *oid);
void snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t);
enum agentx_type snmp_get_varbind_type(const struct agentx_varbind *vb);
//struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o);
struct snmp_registration *snmp_registration_create(struct snmp_proto *p, u8 mib_class);
int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class);
/*
* AgentX - TX buffer manipulation
*/
/* Functions filling buffer a typed value */
struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid);
struct agentx_varbind *snmp_create_varbind_null(byte *buf);
byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val);
@ -64,18 +70,28 @@ byte *snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr);
byte *snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len);
/* Raw */
byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid);
byte *snmp_no_such_instance(byte *buf, struct agentx_varbind *vb, struct oid *oid);
byte *snmp_put_str(byte *buf, const char *str);
byte *snmp_put_nstr(byte *buf, const char *str, uint len);
byte *snmp_put_blank(byte *buf);
byte *snmp_put_oid(byte *buf, struct oid *oid);
byte *snmp_put_ip4(byte *buf, ip4_addr ip4);
byte *snmp_put_fbyte(byte *buf, u8 data);
/*
*
* Helpers, Misc, Debugging
*
*/
struct snmp_registration *snmp_registration_create(struct snmp_proto *p, u8 mib_class);
int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class);
void snmp_dump_packet(byte *pkt, uint size);
void snmp_oid_dump(const struct oid *oid);
enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
int agentx_type_size(enum agentx_type t);
int snmp_test_close_reason(byte value);
struct agentx_header *snmp_create_tx_header(struct snmp_proto *p, byte *rbuf);
int snmp_is_partial(const struct snmp_proto *p);
struct agentx_header *snmp_get_header(const struct snmp_proto *p);
void snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c);
void snmp_unset_header(struct snmp_proto *p);
#endif

92
proto/snmp/splitter.py Executable file
View File

@ -0,0 +1,92 @@
#! /bin/env python3
"""
A very simple script used to test that BIRD does not segfaults on randomly split
AgentX PDUs.
"""
import socket
import random
import time
import math
ADDRESS = "127.0.0.1"
PORT = 5000
AGENTX_MASTER_PORT = 705
def chunks(lst, n):
l = len(lst)
for i in range(0, l, n):
yield lst[i: min(i+n, l)]
def print_msg(header, msg, total):
print(header, "{}/{}".format(len(msg), total))
for line in chunks(msg, 16):
print(" ", end="")
for char in line:
print("0x{:02x} ".format(char), end="")
print()
def create_listen():
tun = socket.socket()
print(f"Binding to port {PORT} on {ADDRESS}")
tun.bind((ADDRESS, PORT))
print(f"Listening on {ADDRESS} port {PORT}")
tun.listen(2)
return tun
def io_loop(rx, tx):
while True:
try:
to_master = rx.recv(8192)
except BlockingIOError:
to_master = None
try:
to_subagent = tx.recv(8192)
except BlockingIOError:
to_subagent = None
if to_master is not None and len(to_master) > 0:
print_msg("S=>M: ", to_master, len(to_master))
tx.send(to_master)
if to_subagent is not None and len(to_subagent) > 0:
limit = 5 * len(to_subagent) / 100
part_len = random.randint(math.ceil(limit),
math.floor(len(to_subagent) - limit))
print(f"M->S: {len(to_subagent[:part_len])}/{len(to_subagent)}")
rx.send(to_subagent[:part_len])
time.sleep(0.4)
print_msg("M=>S: ", to_subagent, len(to_subagent))
rx.send(to_subagent[part_len:])
time.sleep(0.02)
def safe_io_loop(tun):
while True:
try:
rx, addr = tun.accept()
print("Subagent connected")
tx = socket.socket()
tx.connect((ADDRESS, AGENTX_MASTER_PORT))
print("Connected to master agent")
rx.setblocking(False)
tx.setblocking(False)
io_loop(rx, tx)
except BrokenPipeError:
rx.close()
tx.close()
def main():
with create_listen() as listening:
safe_io_loop(listening)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@ -151,7 +151,7 @@ struct agentx_header {
u8 version;
u8 type;
u8 flags;
u8 pad;
u8 reserved; /* always zero filled */
u32 session_id; /* AgentX sessionID established by Open-PDU */
u32 transaction_id; /* last transactionID seen/used */
u32 packet_id; /* last packetID seen/used */
@ -165,13 +165,13 @@ struct oid {
u8 n_subid;
u8 prefix;
u8 include;
u8 pad;
u8 reserved; /* always zero filled */
u32 ids[];
};
struct agentx_varbind {
u16 type;
u16 pad;
u16 reserved; /* always zero filled */
/* oid part */
struct oid name;
/* AgentX variable binding data optionaly here */
@ -205,15 +205,15 @@ STATIC_ASSERT(4 + 2 + 2 + AGENTX_HEADER_SIZE == sizeof(struct agentx_response));
struct agentx_close_pdu {
struct agentx_header h;
u8 reason;
u8 pad1;
u16 pad2;
u8 reserved1; /* reserved u24 */
u16 reserved2; /* whole u24 is always zero filled */
};
struct agentx_un_register_hdr {
u8 timeout;
u8 priority;
u8 range_subid;
u8 pad;
u8 reserved; /* always zero filled */
};
struct agentx_bulk_state {