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

SNMP: Major code improvements

SNMP state changes are now handled by snmp_set_state() functions.

The registration structure and related variables are renamed to remove
confusion.

Manipulation of BGP peers, a reference to BGP protocol structures, is improved
by new functions that encapsulate raw hash table macros (moved from snmp.h).
IPv4 addresses now used by bgp_mib.c because BGP4-MIB does not support IPv6
addresses.

Configuration grammar rules are revised.

We now use DBG() and TRACE() macros to output information about SNMP state
chagnes and about received and transmitted packets.

Pieces of old code are removed, minor bugfixes are included. Large debug string
array are removed.
This commit is contained in:
Vojtech Vilimek 2023-11-15 15:03:55 +01:00
parent 53856e49c9
commit c18e6dd58d
9 changed files with 540 additions and 460 deletions

View File

@ -13,6 +13,18 @@
#include "subagent.h"
#include "bgp_mib.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)
static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
/* BGP_MIB states see enum BGP_INTERNAL_STATES */
@ -95,6 +107,25 @@ bgp_get_candidate(u32 field)
return BGP_INTERNAL_PEER_TABLE_END;
}
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;
}
/**
* snmp_bgp_state - linearize oid from BGP4-MIB
* @oid: prefixed object identifier from BGP4-MIB::bgp subtree
@ -208,9 +239,16 @@ snmp_bgp_reg_ok(struct snmp_proto *p, struct agentx_response *r, struct oid *oid
void
snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response UNUSED *r, struct oid UNUSED *oid)
{
// TODO add more sensible action
snmp_stop_subagent(p);
}
/*
* snmp_bgp_notify_common - common functionaly for BGP4-MIB notifications
* @p: SNMP protocol instance
* @type
*
*/
static void
snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_error[], uint state_val)
{
@ -278,8 +316,14 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
#undef SNMP_OID_SIZE_FROM_LEN
}
/*
* 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(struct bgp_proto *bgp_proto)
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;
@ -302,7 +346,8 @@ snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type)
{
// possibly dangerous
ip4_addr ip4 = ipa_to_ip4(bgp->remote_ip);
char last_error[2] = SNMP_BGP_LAST_ERROR(bgp);
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);
}
@ -329,7 +374,8 @@ snmp_bgp_register(struct snmp_proto *p)
{
/* Register the whole BGP4-MIB::bgp root tree node */
struct snmp_register *registering = snmp_register_create(p, SNMP_BGP4_MIB);
struct snmp_registration *reg;
reg = snmp_registration_create(p, SNMP_BGP4_MIB);
struct oid *oid = mb_alloc(p->pool,
snmp_oid_sizeof(ARRAY_SIZE(bgp_mib_prefix)));
@ -337,10 +383,13 @@ snmp_bgp_register(struct snmp_proto *p)
STORE_U8(oid->prefix, SNMP_MGMT);
memcpy(oid->ids, bgp_mib_prefix, sizeof(bgp_mib_prefix));
registering->oid = oid;
reg->oid = oid;
/* snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance, uint contid) */
snmp_register(p, oid, 1, 0, SNMP_REGISTER_TREE, SNMP_DEFAULT_CONTEXT);
/*
* 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_DEFAULT_CONTEXT);
}
}
@ -374,59 +423,58 @@ ip4_to_oid(struct oid *o, ip4_addr addr)
}
static void
print_bgp_record(const struct bgp_config *config)
print_bgp_record(const struct bgp_proto *bgp_proto)
{
struct proto_config *cf = (struct proto_config *) config;
struct bgp_proto *bgp_proto = (struct bgp_proto *) cf->proto;
//struct proto_config *cf = bgp_proto->p.cf;
struct bgp_conn *conn = bgp_proto->conn;
snmp_log(" name: %s", cf->name);
snmp_log(".");
snmp_log(" rem. identifier: %u", bgp_proto->remote_id);
snmp_log(" local ip: %I", config->local_ip);
snmp_log(" remote ip: %I", config->remote_ip);
snmp_log(" local port: %u", config->local_port);
snmp_log(" remote port: %u", config->remote_port);
DBG(" name: %s", cf->name);
DBG(".");
DBG(" rem. identifier: %u", bgp_proto->remote_id);
DBG(" local ip: %I", config->local_ip);
DBG(" remote ip: %I", config->remote_ip);
DBG(" local port: %u", config->local_port);
DBG(" remote port: %u", config->remote_port);
if (conn) {
snmp_log(" state: %u", conn->state);
snmp_log(" remote as: %u", conn->remote_caps->as4_number);
DBG(" state: %u", conn->state);
DBG(" remote as: %u", conn->remote_caps->as4_number);
}
snmp_log(" in updates: %u", bgp_proto->stats.rx_updates);
snmp_log(" out updates: %u", bgp_proto->stats.tx_updates);
snmp_log(" in total: %u", bgp_proto->stats.rx_messages);
snmp_log(" out total: %u", bgp_proto->stats.tx_messages);
snmp_log(" fsm transitions: %u",
DBG(" in updates: %u", bgp_proto->stats.rx_updates);
DBG(" out updates: %u", bgp_proto->stats.tx_updates);
DBG(" in total: %u", bgp_proto->stats.rx_messages);
DBG(" out total: %u", bgp_proto->stats.tx_messages);
DBG(" fsm transitions: %u",
bgp_proto->stats.fsm_established_transitions);
snmp_log(" fsm total time: -- (0)"); // not supported by bird
snmp_log(" retry interval: %u", config->connect_retry_time);
DBG(" fsm total time: -- (0)"); // not supported by bird
DBG(" retry interval: %u", config->connect_retry_time);
snmp_log(" hold configurated: %u", config->hold_time );
snmp_log(" keep alive config: %u", config->keepalive_time );
DBG(" hold configurated: %u", config->hold_time );
DBG(" keep alive config: %u", config->keepalive_time );
snmp_log(" min AS origin. int.: -- (0)"); // not supported by bird
snmp_log(" min route advertisement: %u", 0 );
snmp_log(" in update elapsed time: %u", 0 );
DBG(" min AS origin. int.: -- (0)"); // not supported by bird
DBG(" min route advertisement: %u", 0 );
DBG(" in update elapsed time: %u", 0 );
if (!conn)
snmp_log(" no connection established");
DBG(" no connection established");
snmp_log(" outgoinin_conn state %u", bgp_proto->outgoing_conn.state + 1);
snmp_log(" incoming_conn state: %u", bgp_proto->incoming_conn.state + 1);
DBG(" outgoinin_conn state %u", bgp_proto->outgoing_conn.state + 1);
DBG(" incoming_conn state: %u", bgp_proto->incoming_conn.state + 1);
}
static void
print_bgp_record_all(struct snmp_proto *p)
{
snmp_log("dumping watched bgp status");
DBG("dumping watched bgp status");
HASH_WALK(p->bgp_hash, next, peer)
{
print_bgp_record(peer->config);
print_bgp_record(peer->bgp_proto);
}
HASH_WALK_END;
snmp_log("dumping watched end");
DBG("dumping watched end");
}
@ -947,9 +995,9 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
uint size = c->size - snmp_varbind_header_size(vb);
byte *pkt;
ip_addr addr;
ip4_addr addr;
if (oid_state_compare(oid, state) == 0 && snmp_bgp_valid_ip4(oid))
addr = ipa_from_ip4(ip4_from_oid(oid));
addr = ip4_from_oid(oid);
else
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
@ -959,39 +1007,44 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
// TODO XXX deal with possible change of (remote) ip; BGP should restart and
// disappear
struct snmp_bgp_peer *pe = HASH_FIND(p->bgp_hash, SNMP_HASH, addr);
struct snmp_bgp_peer *pe = snmp_hash_find(p, addr);
struct bgp_proto *bgp_proto = NULL;
struct proto *proto = NULL;
if (pe)
{
proto = ((struct proto_config *) pe->config)->proto;
if (proto->proto == &proto_bgp &&
ipa_equal(addr, ((struct bgp_proto *) proto)->remote_ip))
{
bgp_proto = (struct bgp_proto *) proto;
}
/* We did not found binded BGP protocol. */
else
{
die("Binded bgp protocol not found!");
vb->type = AGENTX_NO_SUCH_INSTANCE;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
}
else
if (!pe)
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
const struct bgp_proto *bgp_proto = pe->bgp_proto;
if (!ipa_is_ip4(bgp_proto->remote_ip))
{
// TODO XXX: serious issue here
log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name);
vb->type = AGENTX_NO_SUCH_INSTANCE;
c->error = AGENTX_RES_GEN_ERROR;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
ip4_addr proto_ip = ipa_to_ip4(bgp_proto->remote_ip);
if (!ip4_equal(proto_ip, pe->peer_ip))
{
// TODO XXX:
/* Here, we could be in problem as the bgp_proto IP address could be changed */
log(L_ERR, "%s: Stored hash key IP address and peer remote address differ.",
bgp_proto->p.name);
vb->type = AGENTX_NO_SUCH_INSTANCE;
c->error = AGENTX_RES_GEN_ERROR;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
const struct bgp_conn *bgp_conn = bgp_proto->conn;
const struct bgp_stats *bgp_stats = &bgp_proto->stats;
const struct bgp_config *bgp_conf = bgp_proto->cf;
uint bgp_state = snmp_bgp_fsm_state(bgp_proto);
char last_error[2] = SNMP_BGP_LAST_ERROR(bgp_proto);
char last_error[2];
snmp_bgp_last_error(bgp_proto, last_error);
switch (state)
{
case BGP_INTERNAL_PEER_IDENTIFIER:
@ -1006,7 +1059,7 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
break;
case BGP_INTERNAL_ADMIN_STATUS:
if (proto->disabled)
if (bgp_proto->p.disabled)
pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_STOP);
else
pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_START);
@ -1229,3 +1282,40 @@ snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void *tr
}
#endif
/*
* snmp_bgp_start - prepare BGP4-MIB
* @p - SNMP protocol instance holding memory pool
*
* This function create all runtime bindings to BGP procotol structures.
* It is gruaranteed that the BGP protocols exist.
*/
void
snmp_bgp_start(struct snmp_proto *p)
{
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;
if (ipa_zero(bgp_config->remote_ip))
die("unsupported dynamic BGP");
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);
}
}

View File

@ -55,8 +55,7 @@ void snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp)
#define SNMP_BGP_LOCAL_AS 2
#define SNMP_BGP_PEER_TABLE 3
#define SNMP_BGP_PEER_ENTRY 1
/* BGP4-MIB::bgpIdentifier for local identification */
#define SNMP_BGP_IDENTIFIER 4
#define SNMP_BGP_IDENTIFIER 4 /* BGP4-MIB::bgpIdentifier local router id */
/* BGP linearized state */
enum BGP_INTERNAL_STATES {
@ -97,8 +96,4 @@ enum BGP_INTERNAL_STATES {
BGP_INTERNAL_NO_VALUE = 255,
} PACKED;
#define SNMP_BGP_LAST_ERROR(bgp_proto) \
{ bgp_proto->last_error_code & 0x00FF0000 >> 16, \
bgp_proto->last_error_code & 0x000000FF };
#endif

View File

@ -19,46 +19,64 @@ CF_DEFINES
CF_DECLS
CF_KEYWORDS(SNMP, PROTOCOL, BPG, LOCAL, AS, REMOTE, ADDRESS, PORT, DESCRIPTION,
TIMEOUT, PRIORITY, CONTEXT, DEFAULT)
TIMEOUT, PRIORITY, CONTEXT, DEFAULT, MESSAGE)
CF_GRAMMAR
proto: snmp_proto '}' { this_channel = NULL; } ;
proto: snmp_proto ;
snmp_proto:
snmp_proto_start '{'
| snmp_proto proto_item ';'
| snmp_proto snmp_bgp_bond ';'
| snmp_proto LOCAL PORT expr ';' {
if (($4 < 1) || ($4 > 65535)) cf_error("Invalid port number");
SNMP_CFG->local_port = $4;
snmp_proto_start proto_name '{' snmp_proto_opts '}' ;
snmp_proto_item:
proto_item
| snmp_bgp_bond
/* | INTERFACE snmp_interface TODO */
| LOCAL PORT expr {
if (($3 < 1) || ($3 > 65535)) cf_error("Invalid port number");
SNMP_CFG->local_port = $3;
}
| snmp_proto REMOTE PORT expr ';' {
if (($4 < 1) || ($4 > 65535)) cf_error("Invalid port number");
SNMP_CFG->remote_port = $4;
| REMOTE PORT expr {
if (($3 < 1) || ($3 > 65535)) cf_error("Invalid port number");
SNMP_CFG->remote_port = $3;
}
| snmp_proto LOCAL ID ipa ';' { SNMP_CFG->bgp_local_id = $4; }
| snmp_proto LOCAL ADDRESS ipa ';' { SNMP_CFG->local_ip = $4; }
| snmp_proto REMOTE ADDRESS ipa ';' {
if (ipa_zero($4)) cf_error("Invalid remote ip address");
SNMP_CFG->remote_ip = $4;
| LOCAL ID ipa { SNMP_CFG->bgp_local_id = $3; }
| LOCAL ADDRESS ipa { SNMP_CFG->local_ip = $3; }
| REMOTE ADDRESS ipa {
if (ipa_zero($3)) cf_error("Invalid remote ip address");
SNMP_CFG->remote_ip = $3;
}
| snmp_proto LOCAL AS expr ';' {
if ($4 < 1 || $4 > 65535) cf_error("Invalid local AS");
SNMP_CFG->bgp_local_as = $4;
| LOCAL AS expr {
if ($3 < 1 || $3 > 65535) cf_error("Invalid local AS");
SNMP_CFG->bgp_local_as = $3;
}
| snmp_proto DESCRIPTION text ';' {
| SNMP DESCRIPTION text {
if (strlen($3) > UINT32_MAX) cf_error("Description is too long");
SNMP_CFG->description = $3;
}
| snmp_proto TIMEOUT expr ';' {
if ($3 < 1 || $3 > 255) cf_error("Timeout must be in range 1-255");
| PRIORITY expr {
if ($2 > 255) cf_error("Registration priority must be in range 0-255");
SNMP_CFG->priority = $2;
}
| MESSAGE TIMEOUT expr_us {
/* TODO */
if ($3 TO_S > 255) log(L_WARN, "%s: msg", this_proto->name);
if ($3 TO_S < 1) log(L_WARN, "%s: msg", this_proto->name);
if (($3 TO_S) - (int)($3 TO_S) > 0) log(L_WARN, "%s: ", this_proto->name);
SNMP_CFG->timeout = $3;
}
| snmp_proto PRIORITY expr ';' {
if ($3 > 255) cf_error("Registration priority must be in range 0-255");
SNMP_CFG->priority = $3;
/*
| ERROR TIMEOUT expr_us {
/ * TODO * /
SNMP_CFG->error_timeout = $3;
}
*/
| START DELAY TIME expr_us { SNMP_CFG->startup_delay = $4; }
;
snmp_proto_opts:
/* empty */
| snmp_proto_opts snmp_proto_item ';'
;
snmp_proto_start: proto_start SNMP
@ -80,18 +98,25 @@ snmp_proto_start: proto_start SNMP
SNMP_CFG->priority = AGENTX_PRIORITY;
}
proto_name ;
snmp_bgp_bond: BGP symbol
{
/* the snmp_context rule sets the correct value of this_bond */
cf_assert_symbol($2, SYM_PROTO);
if (!$2->proto) cf_error("BGP protocol %s not found", $2->name);
cf_assert($2->proto->protocol == &proto_bgp,
"SNMP BGP bond accepts only BGP protocols");
struct bgp_config *bgp_config = SKIP_BACK(struct bgp_config, c, $2->proto);
if (!ipa_is_ip4(bgp_config->remote_ip))
cf_error("BGP4-MIB does not support IPv6 addresses.");
struct snmp_bond *this_bond = cfg_alloc(sizeof(struct snmp_bond));
this_bond->type = SNMP_BGP;
this_bond->config = $2->proto;
cf_assert_symbol($2, SYM_PROTO);
this_bond->proto = $2->proto;
if (!this_bond->proto) cf_error("BGP protocol %s not found", $2->name);
add_tail(&SNMP_CFG->bgp_entries, &this_bond->n);
SNMP_CFG->bonds++;
}

View File

@ -88,15 +88,146 @@
#include "subagent.h"
#include "snmp_utils.h"
static const char * const snmp_state[] = {
[SNMP_INIT] = "SNMP INIT",
[SNMP_LOCKED] = "SNMP LOCKED",
[SNMP_OPEN] = "SNMP CONNECTION OPENED",
[SNMP_REGISTER] = "SNMP REGISTERING MIBS",
[SNMP_CONN] = "SNMP CONNECTED",
[SNMP_STOP] = "SNMP STOPPING",
[SNMP_DOWN] = "SNMP DOWN",
};
static void snmp_start_locked(struct object_lock *lock);
static void snmp_sock_err(sock *sk, int err);
static void snmp_stop_timeout(timer *tm);
static void snmp_cleanup(struct snmp_proto *p);
/*
* snmp_rx_skip - skip all received data
* @sk: communication socket
* @size: size of received PDUs
*
* Socket rx_hook used when we are reseting the connection due to malformed PDU.
*/
static int
snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
{
return 1;
}
/*
* snmp_tx - handle empty TX-buffer during session reset
* @sk: communication socket
*
* 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.
*/
static void
snmp_tx(sock *sk)
{
struct snmp_proto *p = sk->data;
snmp_sock_disconnect(p, 1);
}
/*
* snmp_set_state - change state with associated actions
* @p - SNMP protocol instance
* @state - new SNMP protocol state
*/
void
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 (last == SNMP_RESET)
rfree(p->sock);
switch (state)
{
case SNMP_INIT:
ASSERT(last == SNMP_DOWN);
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);
break;
case SNMP_LOCKED:
ASSERT(last == SNMP_INIT || SNMP_RESET);
sock *s = sk_new(p->pool);
s->type = SK_TCP_ACTIVE;
s->saddr = p->local_ip;
s->daddr = p->remote_ip;
s->dport = p->remote_port;
s->rbsize = SNMP_RX_BUFFER_SIZE;
s->tbsize = SNMP_TX_BUFFER_SIZE;
/* s->tos = IP_PREC_INTERNET_CONTROL */
s->tx_hook = snmp_connected;
s->err_hook = snmp_sock_err;
p->sock = s;
s->data = p;
/* Try opening the socket, schedule a retry on fail */
if (sk_open(s) < 0)
{
rfree(s);
p->sock = NULL;
tm_start(p->startup_timer, p->timeout);
}
break;
case SNMP_OPEN:
ASSERT(last == SNMP_LOCKED);
p->sock->rx_hook = snmp_rx;
p->sock->tx_hook = NULL;
snmp_start_subagent(p);
// handle no response (for long time)
break;
case SNMP_REGISTER:
ASSERT(last == SNMP_OPEN);
snmp_register_mibs(p);
break;
case SNMP_CONN:
ASSERT(last == SNMP_REGISTER);
proto_notify_state(&p->p, PS_UP);
break;
case SNMP_STOP:
ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
snmp_stop_subagent(p);
p->startup_timer->hook = snmp_stop_timeout;
tm_start(p->startup_timer, p->timeout);
proto_notify_state(&p->p, PS_STOP);
break;
case SNMP_DOWN:
//ASSUME(last == SNMP_STOP || SNMP_INIT || SNMP_LOCKED || SNMP_OPEN);
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;
case SNMP_RESET:
ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
ASSERT(p->sock);
p->sock->rx_hook = snmp_rx_skip;
p->sock->tx_hook = snmp_tx;
break;
default:
die("unknown state transition");
}
}
/*
* snmp_init - preinitialize SNMP instance
@ -110,22 +241,11 @@ snmp_init(struct proto_config *CF)
{
struct proto *P = proto_new(CF);
struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, CF);
p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
p->local_ip = cf->local_ip;
p->remote_ip = cf->remote_ip;
p->local_port = cf->local_port;
p->remote_port = cf->remote_port;
p->bgp_local_as = cf->bgp_local_as;
p->bgp_local_id = cf->bgp_local_id;
snmp_log("changing state to INIT");
p->state = SNMP_INIT;
p->timeout = cf->timeout;
/* default starting state */
//p->state = SNMP_DOWN; // (0)
return P;
}
@ -155,8 +275,8 @@ snmp_cleanup(struct snmp_proto *p)
rfree(p->lock);
p->lock = NULL;
struct snmp_register *r, *r2;
WALK_LIST_DELSAFE(r, r2, p->register_queue)
struct snmp_registration *r, *r2;
WALK_LIST_DELSAFE(r, r2, p->registration_queue)
{
rem_node(&r->n);
mb_free(r);
@ -175,10 +295,9 @@ snmp_cleanup(struct snmp_proto *p)
rfree(p->lp);
p->bgp_trie = NULL;
p->state = SNMP_DOWN;
}
#if 0
/*
* snmp_down - stop the SNMP protocol and free resources
* @p - SNMP protocol instance
@ -192,6 +311,7 @@ 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
@ -204,17 +324,7 @@ void
snmp_connected(sock *sk)
{
struct snmp_proto *p = sk->data;
p->state = SNMP_OPEN;
sk->rx_hook = snmp_rx;
sk->tx_hook = NULL;
//sk->tx_hook = snmp_tx;
snmp_start_subagent(p);
// TODO ping interval <move to do_response()>
tm_set(p->ping_timer, current_time() + p->timeout S);
snmp_set_state(p, SNMP_OPEN);
}
/*
@ -233,18 +343,21 @@ snmp_sock_disconnect(struct snmp_proto *p, int reconnect)
tm_stop(p->ping_timer);
if (!reconnect)
return snmp_down(p);
{
snmp_set_state(p, SNMP_DOWN);
return;
}
proto_notify_state(&p->p, PS_START);
//proto_notify_state(&p->p, PS_START);
rfree(p->sock);
p->sock = NULL;
snmp_log("changing state to LOCKED");
p->state = SNMP_LOCKED;
// 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 S); // TODO make me configurable
//p->startup_timer->hook = snmp_startup_timeout;
//tm_start(p->startup_timer, 4); // TODO make me configurable
}
/*
@ -255,9 +368,9 @@ snmp_sock_disconnect(struct snmp_proto *p, int reconnect)
static void
snmp_sock_err(sock *sk, int UNUSED err)
{
snmp_log("socket error '%s' (errno: %d)", strerror(err), err);
struct snmp_proto *p = sk->data;
TRACE(D_EVENTS, "SNMP socket error %d", err);
snmp_sock_disconnect(p, 1);
}
@ -274,35 +387,15 @@ static void
snmp_start_locked(struct object_lock *lock)
{
struct snmp_proto *p = lock->data;
snmp_log("changing state to LOCKED");
p->state = SNMP_LOCKED;
sock *s = p->sock;
if (!p->bgp_trie)
p->bgp_trie = f_new_trie(p->lp, 0); // TODO user-data attachment size
if (!s)
log(L_INFO "SNMP startup timer or btime %t ? %ld", p->startup_delay, p->startup_timer);
if (p->startup_delay)
{
s = sk_new(p->pool);
s->type = SK_TCP_ACTIVE;
s->saddr = p->local_ip;
s->daddr = p->remote_ip;
s->dport = p->remote_port;
s->rbsize = SNMP_RX_BUFFER_SIZE;
s->tbsize = SNMP_TX_BUFFER_SIZE;
//s->tos = IP_PREC_INTERNET_CONTROL
s->tx_hook = snmp_connected;
s->err_hook = snmp_sock_err;
p->sock = s;
s->data = p;
ASSERT(p->startup_timer);
p->startup_timer->hook = snmp_startup_timeout;
tm_start(p->startup_timer, p->startup_delay);
}
/* Try opening the socket, schedule a retry on fail */
if (sk_open(s) < 0)
tm_set(p->startup_timer, current_time() + p->timeout S);
else
snmp_set_state(p, SNMP_LOCKED);
}
/*
@ -317,14 +410,18 @@ void
snmp_reconnect(timer *tm)
{
struct snmp_proto *p = tm->data;
// TODO
snmp_set_state(p, SNMP_DOWN);
return;
if (p->state == SNMP_STOP ||
p->state == SNMP_DOWN)
return;
// TODO is SNMP_RESET really needed ?
// TO-DO is SNMP_RESET really needed ?
if (p->state == SNMP_INIT ||
p->state == SNMP_RESET)
snmp_startup(p);
//snmp_startup(p);
{}
if (!p->sock)
snmp_start_locked(p->lock);
@ -332,6 +429,7 @@ snmp_reconnect(timer *tm)
snmp_connected(p->sock);
}
#if 0
/*
* snmp_startup - start initialized SNMP protocol
* @p - SNMP protocol to start
@ -344,6 +442,7 @@ snmp_reconnect(timer *tm)
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)
@ -372,20 +471,22 @@ snmp_startup(struct snmp_proto *p)
olock_acquire(lock);
}
#endif
/*
* snmp_startup_timeout - start the initiliazed SNMP protocol
* @tm - the startup_timer holding the SNMP protocol instance.
*
* When the timer rings, the function snmp_startup() is invoked.
* This function is internal and shoudln't be used outside the SNMP module.
* This function is internal and shouldn't be used outside the SNMP module.
* Used when we delaying the start procedure, or we want to resend
* an agentx-Open-PDU for non-responding master agent.
*/
void
snmp_startup_timeout(timer *tm)
{
snmp_startup(tm->data);
struct snmp_proto *p = tm->data;
snmp_set_state(p, SNMP_LOCKED);
}
/*
@ -400,7 +501,8 @@ snmp_startup_timeout(timer *tm)
static void
snmp_stop_timeout(timer *tm)
{
snmp_down(tm->data);
struct snmp_proto *p = tm->data;
snmp_set_state(p, SNMP_DOWN);
}
/*
@ -413,13 +515,7 @@ static void
snmp_ping_timeout(timer *tm)
{
struct snmp_proto *p = tm->data;
if (p->state == SNMP_REGISTER ||
p->state == SNMP_CONN)
{
snmp_ping(p);
tm_set(tm, current_time() + p->timeout S);
}
snmp_ping(p);
}
/*
@ -436,42 +532,35 @@ snmp_start(struct proto *P)
struct snmp_proto *p = (void *) P;
struct snmp_config *cf = (struct snmp_config *) P->cf;
p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0);
p->ping_timer = tm_new_init(p->pool, snmp_ping_timeout, p, 0, 0);
p->local_ip = cf->local_ip;
p->remote_ip = cf->remote_ip;
p->local_port = cf->local_port;
p->remote_port = cf->remote_port;
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
p->startup_delay = cf->startup_delay;
p->pool = p->p.pool;
p->lp = lp_new(p->pool);
p->bgp_trie = f_new_trie(p->lp, 0);
//p->bgp_trie = f_new_trie(lp, cf->bonds); // TODO user-data attachment size
init_list(&p->register_queue);
p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0);
p->ping_timer = tm_new_init(p->pool, snmp_ping_timeout, p, p->timeout, 0);
init_list(&p->registration_queue);
init_list(&p->bgp_registered);
/* We create copy of bonds to BGP protocols. */
HASH_INIT(p->bgp_hash, p->pool, 10);
struct snmp_bond *b;
WALK_LIST(b, cf->bgp_entries)
{
const struct bgp_config *bc = (struct bgp_config *) b->proto;
if (bc && !ipa_zero(bc->remote_ip))
{
struct snmp_bgp_peer *peer = \
mb_allocz(p->pool, sizeof(struct snmp_bgp_peer));
peer->config = bc;
peer->peer_ip = bc->remote_ip;
struct net_addr net;
net_fill_ip4(&net, ipa_to_ip4(peer->peer_ip), IP4_MAX_PREFIX_LENGTH);
trie_add_prefix(p->bgp_trie, &net, IP4_MAX_PREFIX_LENGTH, IP4_MAX_PREFIX_LENGTH);
HASH_INSERT(p->bgp_hash, SNMP_HASH, peer);
}
}
snmp_bgp_start(p);
p->last_header = NULL;
snmp_startup(p);
snmp_set_state(p, SNMP_INIT);
return PS_START;
}
@ -487,6 +576,7 @@ snmp_start(struct proto *P)
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);
const struct snmp_config *old = SKIP_BACK(struct snmp_config, cf, p->p.cf);
@ -496,7 +586,7 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
{
WALK_LIST(b2, old->bgp_entries)
{
if (!strcmp(b1->proto->name, b2->proto->name))
if (!strcmp(b1->config->name, b2->config->name))
goto skip;
}
@ -517,75 +607,24 @@ skip:;
static void
snmp_show_proto_info(struct proto *P)
{
struct snmp_proto *sp = (void *) P;
struct snmp_config *c = (void *) P->cf;
struct snmp_proto *p = (void *) P;
cli_msg(-1006, "");
cli_msg(-1006, " snmp status %s", snmp_state[sp->state]);
cli_msg(-1006, " default local as %u", sp->bgp_local_as);
cli_msg(-1006, " default local id %I4", sp->bgp_local_id);
cli_msg(-1006, "");
cli_msg(-1006, " BGP peers");
cli_msg(-1006, " SNMP state %u", p->state);
cli_msg(-1006, " MIBs");
struct snmp_bond *bond;
WALK_LIST(bond, c->bgp_entries)
// TODO move me into the bgp_mib.c
cli_msg(-1006, " BGP4-MIB");
cli_msg(-1006, " enabled true"); // TODO
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");
HASH_WALK(p->bgp_hash, next, peer)
{
struct proto_config *cf = P->cf;
struct bgp_config *bcf = (struct bgp_config *) cf;
struct proto *p = cf->proto;
struct bgp_proto *bp = (struct bgp_proto *) cf->proto;
struct bgp_conn *conn = bp->conn;
cli_msg(-1006, " name: %s", cf->name);
cli_msg(-1006, "");
cli_msg(-1006, " loc. identifier: %I4", bp->local_id);
cli_msg(-1006, " rem. identifier: %I4", bp->remote_id);
cli_msg(-1006, " admin status: %s", (p->disabled) ? "stop" :
"start");
cli_msg(-1006, " version: 4");
cli_msg(-1006, " local ip: %I4", bcf->local_ip);
cli_msg(-1006, " remote ip: %I4", bcf->remote_ip);
cli_msg(-1006, " local port: %I4", bcf->local_port);
cli_msg(-1006, " remote port: %I4", bcf->remote_port);
/*
if (conn) {
cli_msg(-1006, " state: %u", conn->state);
cli_msg(-1006, " remote as: %u", conn->remote_caps->as4_number);
}
*/
cli_msg(-1006, " in updates: %u", bp->stats.rx_updates);
cli_msg(-1006, " out updates: %u", bp->stats.tx_updates);
cli_msg(-1006, " in total: %u", bp->stats.rx_messages);
cli_msg(-1006, " out total: %u", bp->stats.tx_messages);
cli_msg(-1006, " fsm transitions: %u",
bp->stats.fsm_established_transitions);
cli_msg(-1006, " fsm total time: -- (0)");
cli_msg(-1006, " retry interval: %u", bcf->connect_retry_time);
/*
if (conn) {
cli_msg(-1006, " hold time: %u", conn->hold_time);
cli_msg(-1006, " keep alive: %u", conn->keepalive_time );
}
*/
cli_msg(-1006, " hold configurated: %u", bcf->hold_time );
cli_msg(-1006, " keep alive config: %u", bcf->keepalive_time );
cli_msg(-1006, " min AS origin. int.: -- (0)");
cli_msg(-1006, " min route advertisement: %u", 0 );
cli_msg(-1006, " in update elapsed time: %u", 0 );
if (!conn)
cli_msg(-1006, " no default connection");
cli_msg(-1006, " outgoinin_conn state %u", bp->outgoing_conn.state + 1);
cli_msg(-1006, " incoming_conn state: %u", bp->incoming_conn.state + 1);
cli_msg(-1006, "");
cli_msg(-1006, " protocol name: %s", peer->bgp_proto->p.name);
cli_msg(-1006, " remote IPv4 address: %I4", peer->peer_ip);
}
HASH_WALK_END;
}
/*
@ -615,25 +654,17 @@ snmp_shutdown(struct proto *P)
{
struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
tm_stop(p->ping_timer);
if (p->state == SNMP_OPEN ||
p->state == SNMP_REGISTER ||
if (p->state == SNMP_REGISTER ||
p->state == SNMP_CONN)
{
/* We have a connection established (at leased send out Open-PDU). */
snmp_log("changing state to STOP");
p->state = SNMP_STOP;
p->startup_timer->hook = snmp_stop_timeout;
tm_set(p->startup_timer, current_time() + p->timeout S);
snmp_stop_subagent(p);
snmp_set_state(p, SNMP_STOP);
return PS_STOP;
}
else
{
/* We did not create a connection, we clean the lock and other stuff. */
snmp_cleanup(p);
snmp_set_state(p, SNMP_DOWN);
return PS_DOWN;
}
}

View File

@ -30,31 +30,19 @@
#define SNMP_TX_BUFFER_SIZE 8192
enum snmp_proto_state {
SNMP_DOWN = 0,
SNMP_INIT = 1,
SNMP_LOCKED,
SNMP_OPEN,
SNMP_REGISTER,
SNMP_CONN,
SNMP_STOP,
SNMP_DOWN,
SNMP_RESET,
};
/* hash table macros */
#define SNMP_HASH_KEY(n) n->peer_ip
#define SNMP_HASH_NEXT(n) n->next
#define SNMP_HASH_EQ(ip1, ip2) ipa_equal(ip1, ip2)
#define SNMP_HASH_FN(ip) ipa_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)
struct snmp_bond {
node n;
struct proto_config *proto;
struct proto_config *config;
u8 type;
};
@ -68,7 +56,8 @@ struct snmp_config {
ip_addr bgp_local_id; /* BGP4-MIB related fields */
u32 bgp_local_as;
u8 timeout;
btime timeout;
btime startup_delay;
u8 priority;
//struct iface *iface;
u32 bonds;
@ -81,13 +70,12 @@ struct snmp_config {
#define SNMP_BGP_P_REGISTERED 0x02
struct snmp_bgp_peer {
const struct bgp_config *config;
ip_addr peer_ip;
u8 flags;
const struct bgp_proto *bgp_proto;
ip4_addr peer_ip; /* used as hash key */
struct snmp_bgp_peer *next;
};
struct snmp_register {
struct snmp_registration {
node n;
u8 mib_class;
u32 session_id;
@ -117,7 +105,7 @@ struct snmp_proto {
sock *sock;
void *last_header; /* points to partial PDU header */
u8 timeout; /* timeout is part of MIB registration. It
btime timeout; /* timeout is part of MIB registration. It
specifies how long should the master
agent wait for request responses. */
@ -125,8 +113,8 @@ struct snmp_proto {
u32 transaction_id;
u32 packet_id;
uint register_to_ack; /* counter of pending responses to register-pdu */
list register_queue; /* list containing snmp_register records */
uint registrations_to_ack; /* counter of pending responses to register-pdu */
list registration_queue; /* list containing snmp_register records */
list bgp_registered; /* list of currently registered bgp oids
* (struct snmp_registered_oid) */
@ -136,8 +124,7 @@ struct snmp_proto {
struct tbf rl_gen;
timer *ping_timer;
uint startup_delay;
btime startup_delay;
timer *startup_timer;
u8 state;
};
@ -148,6 +135,7 @@ 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);
#endif

View File

@ -442,10 +442,11 @@ all_same:
return 0;
}
struct snmp_register *
snmp_register_create(struct snmp_proto *p, u8 mib_class)
struct snmp_registration *
snmp_registration_create(struct snmp_proto *p, u8 mib_class)
{
struct snmp_register *r = mb_alloc(p->p.pool, sizeof(struct snmp_register));
struct snmp_registration *r;
r = mb_alloc(p->p.pool, sizeof(struct snmp_registration));
r->n.prev = r->n.next = NULL;
@ -456,13 +457,13 @@ snmp_register_create(struct snmp_proto *p, u8 mib_class)
r->mib_class = mib_class;
add_tail(&p->register_queue, &r->n);
add_tail(&p->registration_queue, &r->n);
return r;
}
int
snmp_register_same(struct snmp_register *r, struct agentx_header *h, u8 class)
snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class)
{
return
(r->mib_class == class) &&
@ -475,10 +476,10 @@ snmp_register_same(struct snmp_register *r, struct agentx_header *h, u8 class)
void UNUSED
snmp_dump_packet(byte UNUSED *pkt, uint size)
{
snmp_log("dump");
DBG("dump");
for (uint i = 0; i < size; i += 4)
snmp_log("pkt [%d] 0x%02x%02x%02x%02x", i, pkt[i],pkt[i+1],pkt[i+2],pkt[i+3]);
snmp_log("end dump");
DBG("pkt [%d] 0x%02x%02x%02x%02x", i, pkt[i],pkt[i+1],pkt[i+2],pkt[i+3]);
DBG("end dump");
}
/*

View File

@ -48,8 +48,8 @@ void snmp_oid_dump(const struct oid *oid);
//struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o);
struct snmp_register *snmp_register_create(struct snmp_proto *p, u8 mib_class);
int snmp_register_same(struct snmp_register *r, struct agentx_header *h, u8 class);
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);
byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);

View File

@ -17,7 +17,7 @@
* Problems
* ------------------------------------------------------------
*
* change of remote ip -> no notification, no update
* change of remote ip -> no notification, no update (be careful in recofing)
* same ip, different ports
* distinct VRF (two interfaces with overlapping private addrs)
* posible link-local addresses in LOCAL_IP
@ -38,6 +38,16 @@
* to be called. The socket's tx_hook is called when the TX-buffer is empty,
* meaning our response (agentx-Response-PDU) was send.
*
*
* Partial parsing
* It may happen that we received only staring part of some PDU from the
* communication socket. In most cases if we recognize this situation we
* immediately return, waiting for rest of the PDU to arrive. But for packets
* like agentx-Get-PDU, agentx-GetNext-PDU and agentx-GetBulk-PDU it could be
* costly as they could hold many VarBinds. In these cases we process.
*
* Transmit packet context
*
*/
static void snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c);
@ -48,61 +58,9 @@ static struct agentx_response *prepare_response(struct snmp_proto *p, struct snm
static void response_err_ind(struct agentx_response *res, enum agentx_response_errs err, u16 ind);
static uint update_packet_size(struct snmp_proto *p, const byte *start, byte *end);
static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result);
static void snmp_tx(sock *sk);
u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
static const char * const snmp_errs[] UNUSED = {
[AGENTX_RES_NO_ERROR ] = "No error",
[AGENTX_RES_GEN_ERROR ] = "General error",
[AGENTX_RES_NO_ACCESS ] = "No access",
[AGENTX_RES_WRONG_TYPE ] = "Wrong type",
[AGENTX_RES_WRONG_LENGTH ] = "Wrong length",
[AGENTX_RES_WRONG_ENCODING ] = "Wrong encoding",
[AGENTX_RES_WRONG_VALUE ] = "Wrong value",
[AGENTX_RES_NO_CREATION ] = "No creation",
[AGENTX_RES_INCONSISTENT_VALUE ] = "Inconsistent value",
[AGENTX_RES_RESOURCE_UNAVAILABLE ] = "Resource unavailable",
[AGENTX_RES_COMMIT_FAILED ] = "Commit failed",
[AGENTX_RES_UNDO_FAILED ] = "Undo failed",
[AGENTX_RES_NOT_WRITABLE ] = "Not writable",
[AGENTX_RES_INCONSISTENT_NAME ] = "Inconsistent name",
[AGENTX_RES_OPEN_FAILED ] = "Open failed",
[AGENTX_RES_NOT_OPEN ] = "Not open",
[AGENTX_RES_INDEX_WRONG_TYPE ] = "Index wrong type",
[AGENTX_RES_INDEX_ALREADY_ALLOC ] = "Index already allocated",
[AGENTX_RES_INDEX_NONE_AVAIL ] = "Index none availlable",
[AGENTX_RES_NOT_ALLOCATED ] = "Not allocated",
[AGENTX_RES_UNSUPPORTED_CONTEXT ] = "Unsupported contex",
[AGENTX_RES_DUPLICATE_REGISTER ] = "Duplicate registration",
[AGENTX_RES_UNKNOWN_REGISTER ] = "Unknown registration",
[AGENTX_RES_UNKNOWN_AGENT_CAPS ] = "Unknown agent caps",
[AGENTX_RES_PARSE_ERROR ] = "Parse error",
[AGENTX_RES_REQUEST_DENIED ] = "Request denied",
[AGENTX_RES_PROCESSING_ERR ] = "Processing error",
};
static const char * const snmp_pkt_type[] UNUSED = {
[AGENTX_OPEN_PDU] = "Open-PDU",
[AGENTX_CLOSE_PDU] = "Close-PDU",
[AGENTX_REGISTER_PDU] = "Register-PDU",
[AGENTX_UNREGISTER_PDU] = "Unregister-PDU",
[AGENTX_GET_PDU] = "Get-PDU",
[AGENTX_GET_NEXT_PDU] = "GetNext-PDU",
[AGENTX_GET_BULK_PDU] = "GetBulk-PDU",
[AGENTX_TEST_SET_PDU] = "TestSet-PDU",
[AGENTX_COMMIT_SET_PDU] = "CommitSet-PDU",
[AGENTX_UNDO_SET_PDU] = "UndoSet-PDU",
[AGENTX_CLEANUP_SET_PDU] = "CleanupSet-PDU",
[AGENTX_NOTIFY_PDU] = "Notify-PDU",
[AGENTX_PING_PDU] = "Ping-PDU",
[AGENTX_INDEX_ALLOCATE_PDU] = "IndexAllocate-PDU",
[AGENTX_INDEX_DEALLOCATE_PDU] = "IndexDeallocate-PDU",
[AGENTX_ADD_AGENT_CAPS_PDU] = "AddAgentCaps-PDU",
[AGENTX_REMOVE_AGENT_CAPS_PDU] = "RemoveAgentCaps-PDU",
[AGENTX_RESPONSE_PDU] = "Response-PDU",
};
static inline void
snmp_header(struct agentx_header *h, enum agentx_pdu_types type, u8 flags)
{
@ -154,7 +112,7 @@ snmp_register_failed(struct snmp_proto *p, struct agentx_response *res, struct o
}
/*
* snmp_register_ack - handle response to agentx-Register-PDU
* snmp_register_ack - handle registration -- response to agentx-Register-PDU
* @p: SNMP protocol instance
* @res: header of agentx-Response-PDU
* @class: MIB subtree associated with agentx-Register-PDU
@ -162,11 +120,11 @@ snmp_register_failed(struct snmp_proto *p, struct agentx_response *res, struct o
void
snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class)
{
struct snmp_register *reg;
WALK_LIST(reg, p->register_queue)
struct snmp_registration *reg;
WALK_LIST(reg, p->registration_queue)
{
// TODO add support for more mib trees (other than BGP)
if (snmp_register_same(reg, &res->h, class))
if (snmp_registration_match(reg, &res->h, class))
{
struct snmp_registered_oid *ro = \
mb_alloc(p->p.pool, sizeof(struct snmp_registered_oid));
@ -177,7 +135,7 @@ snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class)
rem_node(&reg->n);
mb_free(reg);
p->register_to_ack--;
p->registrations_to_ack--;
add_tail(&p->bgp_registered, &ro->n);
@ -190,18 +148,6 @@ snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class)
}
}
/*
* snmp_rx_skip - skip all received data
* @sk: communication socket
* @size: size of received PDUs
*
* Socket rx_hook used when we are reseting the connection due to malformed PDU.
*/
static int
snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
{
return 1;
}
/*
* snmp_error - handle a malformed packet
@ -214,11 +160,7 @@ snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
static inline void
snmp_error(struct snmp_proto *p)
{
snmp_log("changing state to RESET");
p->state = SNMP_RESET;
p->sock->rx_hook = snmp_rx_skip;
p->sock->tx_hook = snmp_tx;
snmp_set_state(p, SNMP_RESET);
}
/*
@ -234,6 +176,7 @@ snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16
struct snmp_pdu c;
snmp_pdu_context(&c, sk);
// XXX: THIS SHOULDN'T HAPPEN! - fix it
if (c.size < sizeof(struct agentx_response))
snmp_manage_tbuf(p, &c);
@ -273,7 +216,17 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
STORE_U32(h->packet_id, 1);
c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description));
c.buffer = snmp_put_fbyte(c.buffer, p->timeout);
if (p->timeout >= 1 TO_US && p->timeout <= 255 TO_US)
/* use p->timeout ceiled up to whole second */
c.buffer = snmp_put_fbyte(c.buffer,
(p->timeout % (1 S) == 0) ? p->timeout TO_S : p->timeout TO_S + 1);
/* out of range fallbacks */
else if (p->timeout < 1 TO_US)
c.buffer = snmp_put_fbyte(c.buffer, (u8) 1);
else /* p->timeout > 255 TO_US */
c.buffer = snmp_put_fbyte(c.buffer, (u8) 255);
c.buffer = snmp_put_oid(c.buffer, oid);
c.buffer = snmp_put_str(c.buffer, cf->description);
@ -457,9 +410,9 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e
struct agentx_un_register_hdr *ur = (struct agentx_un_register_hdr *) c.buffer;
/* do not override timeout */
STORE_U8(ur->timeout, p->timeout);
/* default priority */
/* 0 = do not override session message timeout */
STORE_U8(ur->timeout, 0);
/* use selected priority */
STORE_U8(ur->priority, cf->priority);
STORE_U8(ur->range_subid, (bound > 1) ? index : 0);
STORE_U8(ur->pad, 0);
@ -554,21 +507,26 @@ close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason)
static uint
parse_close_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
{
TRACE(D_PACKETS, "SNMP received agentx-Close-PDU");
byte *pkt = pkt_start;
struct agentx_header *h = (void *) pkt;
ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
uint pkt_size = LOAD_U32(h->payload);
// TODO load c.reason
if (pkt_size != 0)
{
/* The packet is malform, we reset the connnection anyway */
TRACE(D_PACKETS, "SNMP last PDU was malformed (size)");
snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
// TODO: best solution for possibly malicious pkt_size
//return AGENTX_HEADER_SIZE + MIN(size, pkt_size);
return AGENTX_HEADER_SIZE;
}
else
{
snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
//snmp_sock_disconnect(p, 1); // TODO XXX: should we try to reconnect (2nd arg) ??
}
snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
snmp_sock_disconnect(p, 1); // TODO: should we try to reconnect (2nd arg) ??
snmp_set_state(p, SNMP_RESET);
return AGENTX_HEADER_SIZE;
}
@ -787,15 +745,21 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
if (c.error != AGENTX_RES_NO_ERROR)
{
TRACE(D_PACKETS, "last PDU was parsed with error %u", c.error);
response_err_ind(res, c.error, c.index + 1);
snmp_error(p);
}
else if (all_possible)
{
response_err_ind(res, AGENTX_RES_NO_ERROR, 0);
}
else
{
TRACE(D_PACKETS, "SNMP SET action failed (not writable)");
/* This is an recoverable error, we do not need to reset the connection */
//response_err_ind(res, AGENTX_RES_RESOURCE_UNAVAILABLE, c.index + 1);
response_err_ind(res, AGENTX_RES_NOT_WRITABLE, c.index + 1);
}
sk_send(sk, s);
return pkt - pkt_start;
@ -813,6 +777,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
static uint
parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agentx_response_errs err)
{
// TODO renema the function to pdus
byte *pkt = pkt_start;
struct agentx_header *h = (void *) pkt;
ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
@ -820,6 +785,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen
if (pkt_size != 0)
{
TRACE(D_PACKETS, "SNMP received malformed set PDU (size)");
snmp_simple_response(p, AGENTX_RES_PARSE_ERROR, 0);
// TODO: best solution for possibly malicious pkt_size
return MIN(size, pkt_size + AGENTX_HEADER_SIZE);
@ -845,6 +811,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen
c.error = err;
}
TRACE(D_PACKETS, "SNMP received set PDU with error %u", c.error);
response_err_ind(r, c.error, 0);
sk_send(p->sock, AGENTX_HEADER_SIZE);
@ -868,6 +835,7 @@ parse_commit_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
{
// don't forget to free resoures allocated by parse_test_set_pdu()
//mb_free(tr);
TRACE(D_PACKETS, "SNMP received agentx-CommitSet-PDU");
return parse_set_pdu(p, pkt, size, AGENTX_RES_COMMIT_FAILED);
}
@ -884,6 +852,7 @@ parse_undo_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
{
// don't forget to free resources allocated by parse_test_set_pdu()
//mb_free(tr);
TRACE(D_PACKETS, "SNMP received agentx-UndoSet-PDU");
return parse_set_pdu(p, pkt, size, AGENTX_RES_UNDO_FAILED);
}
@ -898,6 +867,7 @@ parse_undo_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
static uint
parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
{
TRACE(D_PACKETS, "SNMP received agentx-CleanupSet-PDU");
(void)p;
//TODO:
// don't forget to free resources allocated by parse_test_set_pdu()
@ -952,6 +922,7 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
.packet_id = p->packet_id,
};
TRACE(D_PACKETS, "SNMP received PDU with unknown session id");
snmp_simple_response(p, AGENTX_RES_NOT_OPEN, 0);
p->session_id = copy.session_id;
@ -970,6 +941,7 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
{
// TODO: shouldn't we return a parseError or genError for
// packets that mustn't have a non-default context ??
TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0);
// TODO: fix the parsed size (as above)
return MIN(size, pkt_size + AGENTX_HEADER_SIZE);
@ -999,7 +971,9 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
return parse_cleanup_set_pdu(p, pkt, size);
default:
/* drop the packet with unknown type silently */
TRACE(D_PACKETS, "SNMP received unknown packet with type %u", h->type);
snmp_set_state(p, SNMP_DOWN);
// TODO reset connection here
return AGENTX_HEADER_SIZE;
}
}
@ -1027,54 +1001,35 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
if (size < pkt_size + AGENTX_HEADER_SIZE)
return 0;
TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error);
switch (r->error)
{
case AGENTX_RES_NO_ERROR:
do_response(p, res, size);
break;
case AGENTX_RES_NOT_OPEN:
if (p->state == SNMP_LOCKED || p->state == SNMP_INIT)
snmp_startup(p);
else
snmp_connected(p->sock);
break;
case AGENTX_RES_OPEN_FAILED:
if (p->state == SNMP_LOCKED || p->state == SNMP_INIT)
{
ASSUME(p->startup_timer);
p->startup_timer->hook = snmp_startup_timeout;
// TODO: better timeout
tm_set(p->startup_timer, current_time() + p->timeout S);
}
else
{
ASSUME(p->startup_timer);
p->startup_timer->hook = snmp_reconnect;
// TODO: better timeout
tm_set(p->startup_timer, current_time() + p->timeout S);
}
break;
/* Registration errors */
case AGENTX_RES_DUPLICATE_REGISTER:
case AGENTX_RES_REQUEST_DENIED:
case AGENTX_RES_UNKNOWN_REGISTER:
// TODO: more direct path to mib-specifiec code
snmp_register_ack(p, r, size);
break;
/* We are trying to unregister a MIB, the unknownRegistration has same
* effect as success */
case AGENTX_RES_UNKNOWN_REGISTER:
/*
* We found ourselves in an unexpected situation. To enter a well defined
* state as well as give the AgentX master agent room to fix the errors on
* his side, we perform a hard reset of the connections.
*/
case AGENTX_RES_NOT_OPEN:
case AGENTX_RES_OPEN_FAILED:
case AGENTX_RES_UNKNOWN_AGENT_CAPS:
case AGENTX_RES_UNSUPPORTED_CONTEXT:
case AGENTX_RES_UNSUPPORTED_CONTEXT: /* currently we don't use contexts */
case AGENTX_RES_PARSE_ERROR:
case AGENTX_RES_PROCESSING_ERR:
default:
/* erronous packet should be dropped quietly */
// TODO correct error?
snmp_log("received response with error '%s'", snmp_errs[get_u16(&r->error)]);
DBG("SNMP agentx-Response-PDU with unexpected error %u", r->error);
snmp_set_state(p, SNMP_DOWN);
break;
}
@ -1085,7 +1040,7 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
* snmp_register_mibs - register all MIB subtrees
* @p: SNMP protocol instance
*/
static void
void
snmp_register_mibs(struct snmp_proto *p)
{
snmp_bgp_register(p);
@ -1119,12 +1074,12 @@ do_response(struct snmp_proto *p, byte *buf, uint size)
p->session_id = LOAD_U32(h->session_id);
refresh_ids(p, h);
tm_start(p->ping_timer, 0);
/* the state needs to be changed before sending registering PDUs to
* use correct do_response action on them
*/
snmp_log("changing state to REGISTER");
p->state = SNMP_REGISTER;
snmp_register_mibs(p);
snmp_set_state(p, SNMP_REGISTER);
break;
case SNMP_REGISTER:;
@ -1135,12 +1090,8 @@ do_response(struct snmp_proto *p, byte *buf, uint size)
snmp_register_ack(p, r, snmp_get_mib_class(oid));
if (p->register_to_ack == 0)
{
snmp_log("changing state to CONNECTED");
p->state = SNMP_CONN;
proto_notify_state(&p->p, PS_UP);
}
if (p->registrations_to_ack == 0)
snmp_set_state(p, SNMP_CONN);
break;
case SNMP_CONN:
@ -1556,6 +1507,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
uint s = update_packet_size(p, (byte *) res, c.buffer);
/* We send the message in TX-buffer. */
p->last_header = NULL;
sk_send(sk, s);
// TODO think through the error state
@ -1580,9 +1532,15 @@ wait:
error:
if (c.index > UINT16_MAX)
{
TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU");
snmp_simple_response(p, AGENTX_RES_GEN_ERROR, UINT16_MAX);
}
else
{
TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c.error);
snmp_simple_response(p, c.error, c.index);
}
free:
mb_free(o_start);
@ -1617,10 +1575,8 @@ snmp_start_subagent(struct snmp_proto *p)
void
snmp_stop_subagent(struct snmp_proto *p)
{
if (p->state == SNMP_OPEN ||
p->state == SNMP_REGISTER ||
p->state == SNMP_CONN)
close_pdu(p, AGENTX_CLOSE_SHUTDOWN);
tm_stop(p->ping_timer);
close_pdu(p, AGENTX_CLOSE_SHUTDOWN);
}
/*
@ -1663,20 +1619,6 @@ snmp_rx(sock *sk, uint size)
return 1;
}
/*
* snmp_tx - handle empty TX-buffer during session reset
* @sk: communication socket
*
* 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.
*/
static void
snmp_tx(sock *sk)
{
struct snmp_proto *p = sk->data;
snmp_sock_disconnect(p, 1);
}
/*
* snmp_ping - send an agentx-Ping-PDU
@ -1747,6 +1689,8 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_
struct oid *o_curr, struct snmp_pdu UNUSED *c,
enum snmp_search_res *result)
{
// TODO flip retval and result (maybe on more place to stay consistent)
// TODO remove unnecessary o_start/o_curr duplication
ASSUME(o_start != NULL);
if (o_curr && (o_curr->n_subid < 2 || o_curr->ids[0] != 1))
@ -1758,7 +1702,7 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_
{
o_curr = snmp_oid_duplicate(p->pool, o_start);
// XXX is it right time to free o_start right now (here) ?
// not for use in snmp_get_next2() the o_start comes and ends in _gets2_()
// not for use in snmp_get_next2() the o_start comes and ends in _gets_()
}
const struct oid *blank = NULL;
@ -1904,10 +1848,13 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c)
void
snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c)
{
/*
sock *sk = p->sock;
sk_set_tbsize(sk, sk->tbsize + 2048);
// XXX buffer snmp_pdu pointer
c->size += 2048;
*/
}
/*

View File

@ -309,6 +309,9 @@ void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c);
struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o);
u8 snmp_get_mib_class(const struct oid *oid);
void snmp_register_mibs(struct snmp_proto *p);
void snmp_bgp_start(struct snmp_proto *p);
// debug wrapper
#if 0