diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index c2c20bfd..7eb7d613 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -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); + } +} diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index 52bf13f6..c3eaa987 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -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 diff --git a/proto/snmp/config.Y b/proto/snmp/config.Y index d8fa3319..05e7cf38 100644 --- a/proto/snmp/config.Y +++ b/proto/snmp/config.Y @@ -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++; } diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index ee868825..25280329 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -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 - 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; } } diff --git a/proto/snmp/snmp.h b/proto/snmp/snmp.h index dc4b7506..eecd01c3 100644 --- a/proto/snmp/snmp.h +++ b/proto/snmp/snmp.h @@ -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 diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 9cd22bae..64a9ce64 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -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"); } /* diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index b39a22e1..fe371ee0 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -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); diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index d915bb5a..50900d1c 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -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(®->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; + */ } /* diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index f4af725b..9a263a28 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -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