diff --git a/proto/snmp/bgp4_mib.c b/proto/snmp/bgp4_mib.c index 5f4c703d..fabe7cfb 100644 --- a/proto/snmp/bgp4_mib.c +++ b/proto/snmp/bgp4_mib.c @@ -67,6 +67,32 @@ snmp_bgp_reg_failed(struct snmp_proto *p, const struct agentx_response *res, str snmp_reset(p); } +/* + * snmp_bgp_fsm_state - extract BGP FSM state for SNMP BGP4-MIB + * @bgp_proto: BGP instance + * + * Return FSM state in BGP4-MIB encoding + */ +static inline uint +snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto) +{ + const struct bgp_conn *bgp_conn = bgp_proto->conn; + const struct bgp_conn *bgp_in = &bgp_proto->incoming_conn; + const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn; + + if (bgp_conn) + return bgp_conn->state + 1; + + if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE && + MIN(bgp_in->state, bgp_out->state) != BS_CLOSE) + return MIN(bgp_in->state, bgp_out->state) + 1; + if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE) + return BS_IDLE; + + return MAX(bgp_in->state, bgp_out->state) + 1; +} + + /* * snmp_bgp_notify_common - common functionaly for BGP4-MIB notifications * @p: SNMP protocol instance @@ -173,31 +199,6 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ #undef OID_N_SUBID } -/* - * snmp_bgp_fsm_state - extract BGP FSM state for SNMP BGP4-MIB - * @bgp_proto: BGP instance - * - * Return FSM state in BGP4-MIB encoding - */ -static inline uint -snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto) -{ - const struct bgp_conn *bgp_conn = bgp_proto->conn; - const struct bgp_conn *bgp_in = &bgp_proto->incoming_conn; - const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn; - - if (bgp_conn) - return bgp_conn->state + 1; - - if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE && - MIN(bgp_in->state, bgp_out->state) != BS_CLOSE) - return MIN(bgp_in->state, bgp_out->state) + 1; - if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE) - return BS_IDLE; - - return MAX(bgp_in->state, bgp_out->state) + 1; -} - static void snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type) { @@ -209,38 +210,18 @@ snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type) snmp_bgp_notify_common(p, type, ip4, last_error, state_val); } -void +void UNUSED snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp) { snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_ESTABLISHED_NOTIFICATION); } -void +void UNUSED snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp) { snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_BACKWARD_TRANS_NOTIFICATION); } -void -snmp_bgp4_register(struct snmp_proto *p) -{ - /* Register the whole BGP4-MIB::bgp root tree node */ - struct snmp_registration *reg; - reg = snmp_registration_create(p, BGP4_MIB_ID); - - struct oid *oid = mb_allocz(p->pool, sizeof(bgp4_mib_oid)); - memcpy(oid, &bgp4_mib_oid, sizeof(bgp4_mib_oid)); - - reg->reg_hook_ok = NULL; - reg->reg_hook_fail = snmp_bgp_reg_failed; - - /* - * We set both upper bound and index to zero, therefore only single OID - * is being registered. - */ - snmp_register(p, oid, 0, 0, SNMP_REGISTER_TREE); -} - static int snmp_bgp_valid_ip4(const struct oid *o) { @@ -269,48 +250,6 @@ ip4_to_oid(struct oid *o, ip4_addr addr) o->ids[8] = (tmp & 0x000000FF) >> 0; } -static void UNUSED -print_bgp_record(const struct bgp_proto *bgp_proto) -{ - struct bgp_conn *conn = bgp_proto->conn; - - 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) { - DBG(" state: %u", conn->state); - DBG(" remote as: %u", conn->remote_caps->as4_number); - } - - 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); - - DBG(" fsm total time: -- (0)"); // not supported by bird - DBG(" retry interval: %u", config->connect_retry_time); - - DBG(" hold configurated: %u", config->hold_time ); - DBG(" keep alive config: %u", config->keepalive_time ); - - 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) - DBG(" no connection established"); - - DBG(" outgoinin_conn state %u", bgp_proto->outgoing_conn.state + 1); - DBG(" incoming_conn state: %u", bgp_proto->incoming_conn.state + 1); -} - static inline enum snmp_search_res populate_bgp4(struct snmp_pdu *c, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn **conn, const struct bgp_stats **stats, const struct bgp_config **config) @@ -832,6 +771,27 @@ snmp_bgp4_show_info(struct snmp_proto *p) HASH_WALK_END; } +void +snmp_bgp4_register(struct snmp_proto *p) +{ + /* Register the whole BGP4-MIB::bgp root tree node */ + struct snmp_registration *reg; + reg = snmp_registration_create(p, BGP4_MIB_ID); + + struct oid *oid = mb_allocz(p->pool, sizeof(bgp4_mib_oid)); + memcpy(oid, &bgp4_mib_oid, sizeof(bgp4_mib_oid)); + + reg->reg_hook_ok = NULL; + reg->reg_hook_fail = snmp_bgp_reg_failed; + + /* + * We set both upper bound and index to zero, therefore only single OID + * is being registered. + */ + snmp_register(p, oid, 0, 0, SNMP_REGISTER_TREE); +} + + /* * snmp_bgp4_start - prepare BGP4-MIB * @p: SNMP protocol instance holding memory pool diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index fbc2cf0b..eb01fabc 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -112,12 +112,6 @@ #include "bgp4_mib.h" const char agentx_master_addr[] = AGENTX_MASTER_ADDR; -const struct oid *agentx_available_mibs[AGENTX_MIB_COUNT + 1] = { 0 }; - -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); static const char *snmp_state_str[] = { [SNMP_INIT] = "acquiring address lock", @@ -129,6 +123,109 @@ static const char *snmp_state_str[] = { [SNMP_DOWN] = "protocol down", }; + +/* + * Callbacks + */ + +/* + * snmp_sock_err - handle errors on socket by reopenning the socket + * @sk: socket owned by SNMP protocol instance + * @err: socket error code + */ +static void +snmp_sock_err(sock *sk, int UNUSED err) +{ + struct snmp_proto *p = sk->data; + if (err != 0) + TRACE(D_EVENTS, "SNMP socket error (%d)", err); + snmp_set_state(p, SNMP_DOWN); +} + +/* + * snmp_ping_timeout - send a agentx-Ping-PDU + * @tm: the ping_timer holding the SNMP protocol instance. + * + * Send an agentx-Ping-PDU. This function is periodically called by ping + * timer. + */ +static void +snmp_ping_timeout(timer *tm) +{ + struct snmp_proto *p = tm->data; + snmp_ping(p); +} + + +/* + * snmp_stop_timeout - a timeout for non-responding master agent + * @tm: the startup_timer holding the SNMP protocol instance. + * + * We are trying to empty the TX buffer of communication socket. But if it is + * not done in reasonable amount of time, the function is called by timeout + * timer. We down the whole SNMP protocol with cleanup of associated data + * structures. + */ +static void +snmp_stop_timeout(timer *tm) +{ + struct snmp_proto *p = tm->data; + snmp_set_state(p, SNMP_DOWN); +} + +/* + * snmp_connected - start AgentX session on created socket + * @sk: socket owned by SNMP protocol instance + * + * Starts the AgentX communication by sending an agentx-Open-PDU. + * This function is internal and shouldn't be used outside the SNMP module. + */ +void +snmp_connected(sock *sk) +{ + struct snmp_proto *p = sk->data; + snmp_set_state(p, SNMP_OPEN); +} + +/* + * snmp_start_locked - open the socket on locked address + * @lock: object lock guarding the communication mean (address, ...) + * + * This function is called when the object lock is acquired. Main goal is to set + * socket parameters and try to open configured socket. Function + * snmp_connected() handles next stage of SNMP protocol start. When the socket + * coundn't be opened, a new try is scheduled after a small delay. + */ +static void +snmp_start_locked(struct object_lock *lock) +{ + struct snmp_proto *p = lock->data; + if (p->startup_delay) + { + ASSERT(p->startup_timer); + p->startup_timer->hook = snmp_startup_timeout; + tm_start(p->startup_timer, p->startup_delay); + } + else + snmp_set_state(p, SNMP_LOCKED); +} + +/* + * 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 shouldn't be used outside the SNMP module. + * Used when we delaying the start procedure, or we want to retry opening + * the communication socket. + */ +void +snmp_startup_timeout(timer *tm) +{ + struct snmp_proto *p = tm->data; + snmp_set_state(p, SNMP_LOCKED); +} + /* * snmp_rx_skip - skip all received data * @sk: communication socket @@ -158,6 +255,51 @@ snmp_tx_skip(sock *sk) snmp_set_state(p, SNMP_STOP); } +/* + * snmp_cleanup - free all resources allocated by SNMP protocol + * @p: SNMP protocol instance + * + * This function forcefully stops and cleans all resources and memory acqiured + * by given SNMP protocol instance, such as timers, lists, hash tables etc. + */ +static inline void +snmp_cleanup(struct snmp_proto *p) +{ + /* Function tm_stop() is called inside rfree() */ + rfree(p->startup_timer); + p->startup_timer = NULL; + + rfree(p->ping_timer); + p->ping_timer = NULL; + + rfree(p->sock); + p->sock = NULL; + + rfree(p->lock); + p->lock = NULL; + + struct snmp_registration *r, *r2; + WALK_LIST_DELSAFE(r, r2, p->registration_queue) + { + rem_node(&r->n); + mb_free(r); + r = NULL; + } + + HASH_FREE(p->bgp_hash); + rfree(p->lp); + p->lp = NULL; + /* bgp_trie is allocated exclusively from linpool lp */ + p->bgp_trie = NULL; + + struct mib_walk_state *walk = tmp_alloc(sizeof(struct mib_walk_state)); + mib_tree_walk_init(walk, p->mib_tree); + (void) mib_tree_delete(p->mib_tree, walk); + p->mib_tree = NULL; + + p->state = SNMP_DOWN; +} + /* * snmp_set_state - change state with associated actions * @p: SNMP protocol instance @@ -303,84 +445,6 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state) } } -/* - * snmp_init - preinitialize SNMP instance - * @CF: SNMP configuration generic handle - * - * Returns a generic handle pointing to preinitialized SNMP procotol - * instance. - */ -static struct proto * -snmp_init(struct proto_config *CF) -{ - struct proto *P = proto_new(CF); - struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P); - - p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS; - p->state = SNMP_DOWN; - - return P; -} - -/* - * snmp_cleanup - free all resources allocated by SNMP protocol - * @p: SNMP protocol instance - * - * This function forcefully stops and cleans all resources and memory acqiured - * by given SNMP protocol instance, such as timers, lists, hash tables etc. - */ -static inline void -snmp_cleanup(struct snmp_proto *p) -{ - /* Function tm_stop() is called inside rfree() */ - rfree(p->startup_timer); - p->startup_timer = NULL; - - rfree(p->ping_timer); - p->ping_timer = NULL; - - rfree(p->sock); - p->sock = NULL; - - rfree(p->lock); - p->lock = NULL; - - struct snmp_registration *r, *r2; - WALK_LIST_DELSAFE(r, r2, p->registration_queue) - { - rem_node(&r->n); - mb_free(r); - r = NULL; - } - - HASH_FREE(p->bgp_hash); - rfree(p->lp); - p->lp = NULL; - /* bgp_trie is allocated exclusively from linpool lp */ - p->bgp_trie = NULL; - - struct mib_walk_state *walk = tmp_alloc(sizeof(struct mib_walk_state)); - mib_tree_walk_init(walk, p->mib_tree); - (void) mib_tree_delete(p->mib_tree, walk); - p->mib_tree = NULL; - - p->state = SNMP_DOWN; -} - -/* - * snmp_connected - start AgentX session on created socket - * @sk: socket owned by SNMP protocol instance - * - * Starts the AgentX communication by sending an agentx-Open-PDU. - * This function is internal and shouldn't be used outside the SNMP module. - */ -void -snmp_connected(sock *sk) -{ - struct snmp_proto *p = sk->data; - snmp_set_state(p, SNMP_OPEN); -} - /* * snmp_reset - reset AgentX session * @p: SNMP protocol instance @@ -409,129 +473,31 @@ snmp_up(struct snmp_proto *p) } /* - * snmp_sock_err - handle errors on socket by reopenning the socket - * @sk: socket owned by SNMP protocol instance - * @err: socket error code - */ -static void -snmp_sock_err(sock *sk, int UNUSED err) -{ - struct snmp_proto *p = sk->data; - if (err != 0) - TRACE(D_EVENTS, "SNMP socket error (%d)", err); - snmp_set_state(p, SNMP_DOWN); -} - -/* - * snmp_start_locked - open the socket on locked address - * @lock: object lock guarding the communication mean (address, ...) - * - * This function is called when the object lock is acquired. Main goal is to set - * socket parameters and try to open configured socket. Function - * snmp_connected() handles next stage of SNMP protocol start. When the socket - * coundn't be opened, a new try is scheduled after a small delay. - */ -static void -snmp_start_locked(struct object_lock *lock) -{ - struct snmp_proto *p = lock->data; - if (p->startup_delay) - { - ASSERT(p->startup_timer); - p->startup_timer->hook = snmp_startup_timeout; - tm_start(p->startup_timer, p->startup_delay); - } - else - snmp_set_state(p, SNMP_LOCKED); -} - -/* - * 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 shouldn't be used outside the SNMP module. - * Used when we delaying the start procedure, or we want to retry opening - * the communication socket. - */ -void -snmp_startup_timeout(timer *tm) -{ - struct snmp_proto *p = tm->data; - snmp_set_state(p, SNMP_LOCKED); -} - -/* - * snmp_stop_timeout - a timeout for non-responding master agent - * @tm: the startup_timer holding the SNMP protocol instance. - * - * We are trying to empty the TX buffer of communication socket. But if it is - * not done in reasonable amount of time, the function is called by timeout - * timer. We down the whole SNMP protocol with cleanup of associated data - * structures. - */ -static void -snmp_stop_timeout(timer *tm) -{ - struct snmp_proto *p = tm->data; - snmp_set_state(p, SNMP_DOWN); -} - -/* - * snmp_ping_timeout - send a agentx-Ping-PDU - * @tm: the ping_timer holding the SNMP protocol instance. - * - * Send an agentx-Ping-PDU. This function is periodically called by ping - * timer. - */ -static void -snmp_ping_timeout(timer *tm) -{ - struct snmp_proto *p = tm->data; - snmp_ping(p); -} - -/* - * snmp_start - Initialize the SNMP protocol instance + * snmp_shutdown - Forcefully stop the SNMP protocol instance * @P: SNMP protocol generic handle * - * The first step in AgentX subagent startup is protocol initialition. - * We must prepare lists, find BGP peers and finally asynchronously start - * a AgentX subagent session. + * Simple cast-like wrapper around snmp_reset(), see more info there. */ static int -snmp_start(struct proto *P) +snmp_shutdown(struct proto *P) +{ + struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P); + return snmp_reset(p); +} + +/* + * snmp_show_proto_info - print basic information about SNMP protocol instance + * @P: SNMP protocol generic handle + */ +static void +snmp_show_proto_info(struct proto *P) { struct snmp_proto *p = (void *) P; - struct snmp_config *cf = (struct snmp_config *) P->cf; - 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; - p->startup_delay = cf->startup_delay; - p->verbose = cf->verbose; + cli_msg(-1006, " SNMP state: %s", snmp_state_str[p->state]); + cli_msg(-1006, " MIBs"); - p->pool = p->p.pool; - p->lp = lp_new(p->pool); - p->bgp_trie = f_new_trie(p->lp, 0); - p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree)); - - 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); - - /* We create copy of bonds to BGP protocols. */ - HASH_INIT(p->bgp_hash, p->pool, 10); - - mib_tree_init(p->pool, p->mib_tree); - snmp_bgp4_start(p, 1); - - return snmp_set_state(p, SNMP_INIT); + snmp_bgp4_show_info(p); } /* @@ -614,18 +580,65 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF) } /* - * snmp_show_proto_info - print basic information about SNMP protocol instance + * snmp_start - Initialize the SNMP protocol instance * @P: SNMP protocol generic handle + * + * The first step in AgentX subagent startup is protocol initialition. + * We must prepare lists, find BGP peers and finally asynchronously start + * a AgentX subagent session. */ -static void -snmp_show_proto_info(struct proto *P) +static int +snmp_start(struct proto *P) { struct snmp_proto *p = (void *) P; + struct snmp_config *cf = (struct snmp_config *) P->cf; - cli_msg(-1006, " SNMP state: %s", snmp_state_str[p->state]); - cli_msg(-1006, " MIBs"); + 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; + p->startup_delay = cf->startup_delay; + p->verbose = cf->verbose; - snmp_bgp4_show_info(p); + p->pool = p->p.pool; + p->lp = lp_new(p->pool); + p->bgp_trie = f_new_trie(p->lp, 0); + p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree)); + + 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); + + /* We create copy of bonds to BGP protocols. */ + HASH_INIT(p->bgp_hash, p->pool, 10); + + mib_tree_init(p->pool, p->mib_tree); + snmp_bgp4_start(p, 1); + + return snmp_set_state(p, SNMP_INIT); +} + +/* + * snmp_init - preinitialize SNMP instance + * @CF: SNMP configuration generic handle + * + * Returns a generic handle pointing to preinitialized SNMP procotol + * instance. + */ +static struct proto * +snmp_init(struct proto_config *CF) +{ + struct proto *P = proto_new(CF); + struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P); + + p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS; + p->state = SNMP_DOWN; + + return P; } /* @@ -642,19 +655,6 @@ snmp_postconfig(struct proto_config *CF) cf_error("local as not specified"); } -/* - * snmp_shutdown - Forcefully stop the SNMP protocol instance - * @P: SNMP protocol generic handle - * - * Simple cast-like wrapper around snmp_reset(), see more info there. - */ -static int -snmp_shutdown(struct proto *P) -{ - struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P); - return snmp_reset(p); -} - /* * Protocol infrastructure @@ -670,8 +670,8 @@ struct protocol proto_snmp = { .init = snmp_init, .start = snmp_start, .reconfigure = snmp_reconfigure, - .shutdown = snmp_shutdown, .show_proto_info = snmp_show_proto_info, + .shutdown = snmp_shutdown, }; void diff --git a/proto/snmp/snmp.h b/proto/snmp/snmp.h index 9a787c6e..b60b0af9 100644 --- a/proto/snmp/snmp.h +++ b/proto/snmp/snmp.h @@ -53,6 +53,15 @@ enum snmp_transport_type { SNMP_TRANS_TCP, }; +#define SNMP_BGP_P_REGISTERING 0x01 +#define SNMP_BGP_P_REGISTERED 0x02 + +struct snmp_bgp_peer { + const struct bgp_proto *bgp_proto; + ip4_addr peer_ip; /* used as hash key */ + struct snmp_bgp_peer *next; +}; + struct snmp_config { struct proto_config cf; enum snmp_transport_type trans_type; @@ -79,21 +88,6 @@ struct snmp_config { int verbose; }; -#define SNMP_BGP_P_REGISTERING 0x01 -#define SNMP_BGP_P_REGISTERED 0x02 - -struct snmp_bgp_peer { - const struct bgp_proto *bgp_proto; - ip4_addr peer_ip; /* used as hash key */ - struct snmp_bgp_peer *next; -}; - -struct snmp_registered_oid { - node n; - struct oid *oid; -}; - - struct snmp_proto { struct proto p; struct object_lock *lock; @@ -165,7 +159,6 @@ void snmp_connected(sock *sk); void snmp_startup_timeout(timer *tm); void snmp_reconnect(timer *tm); int snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state); - int snmp_reset(struct snmp_proto *p); void snmp_up(struct snmp_proto *p); diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 10febdd6..42de8f99 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -36,12 +36,6 @@ snmp_session(const struct snmp_proto *p, struct agentx_header *h) STORE_U32(h->packet_id, p->packet_id); } -inline int -snmp_has_context(const struct agentx_header *h) -{ - return h->flags & AGENTX_NON_DEFAULT_CONTEXT; -} - inline void * snmp_varbind_data(const struct agentx_varbind *vb) { @@ -49,45 +43,6 @@ snmp_varbind_data(const struct agentx_varbind *vb) return (void *) &vb->name + name_size; } -struct oid * -snmp_varbind_set_name_len(struct snmp_pdu *c, struct agentx_varbind **vb, u8 len) -{ - struct oid *oid = &(*vb)->name; - - if (oid->n_subid >= len) - { - c->size += (oid->n_subid - len) * sizeof(u32); - oid->n_subid = len; - return oid; - } - - /* We need more space */ - ASSUME(len >= oid->n_subid); - uint diff_size = (len - oid->n_subid) * sizeof(u32); - - if (snmp_tbuf_reserve(c, diff_size)) - oid = &(*vb)->name; - - ASSERT(c->size >= diff_size); - c->size -= diff_size; - oid->n_subid = len; - return &(*vb)->name; -} - -void -snmp_varbind_duplicate_hdr(struct snmp_pdu *c, struct agentx_varbind **vb) -{ - ASSUME(vb != NULL && *vb != NULL); - uint hdr_size = snmp_varbind_header_size(&(*vb)->name); - (void) snmp_tbuf_reserve(c, hdr_size); - - ASSERT(c->size >= hdr_size); - byte *buffer = c->buffer; - ADVANCE(c->buffer, c->size, hdr_size); - memcpy(buffer, *vb, hdr_size); - *vb = (struct agentx_varbind *) buffer; -} - /** * snmp_is_oid_empty - check if oid is null-valued * @oid: object identifier to check @@ -128,18 +83,6 @@ snmp_oid_is_prefixable(const struct oid *oid) return 1; } - -/** - * snmp_pkt_len - returns size of SNMP packet payload (without header) - * @buf: packet first byte - * @pkt: first byte past packet end - */ -uint -snmp_pkt_len(const byte *start, const byte *end) -{ - return (end - start) - AGENTX_HEADER_SIZE; -} - /* * snmp_oid_copy - copy OID from one place to another * @dest: destination to use @@ -190,29 +133,6 @@ snmp_oid_to_buf(struct oid *dst, const struct oid *src) STORE_U32(dst->ids[i], src->ids[i]); } -/* - * snmp_oid_duplicate - duplicate an OID from memory pool - * @pool: pool to use - * @oid: OID to be duplicated - */ -struct oid * -snmp_oid_duplicate(pool *pool, const struct oid *oid) -{ - struct oid *res = mb_alloc(pool, snmp_oid_size(oid)); - memcpy(res, oid, snmp_oid_size(oid)); - return res; -} - -/** - * snmp_oid_blank - create new null oid (blank) - * @p: pool hodling snmp_proto structure - */ -struct oid * -snmp_oid_blank(struct snmp_proto *p) -{ - return mb_allocz(p->p.pool, sizeof(struct oid)); -} - /** * snmp_str_size_from_len - return in-buffer octet string size * @len: length of C-string, returned from strlen() @@ -278,12 +198,6 @@ snmp_varbind_header_size(const struct oid *vb_name) return snmp_oid_size(vb_name) + OFFSETOF(struct agentx_varbind, name); } -/* - * Beware that for octet string, using this function may be a bit tricky due to - * the different byte orders cpu native/packet - * - * - */ uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb) { @@ -316,66 +230,6 @@ snmp_varbind_size_unsafe(const struct agentx_varbind *vb) } } -/** - * snmp_varbind_size - get size of in-buffer VarBind - * @vb: VarBind in cpu native byte order to measure - * @limit: upper limit of bytes that can be used - * - * This functions assumes valid VarBind type. - * Return 0 for Varbinds longer than limit, Varbind's size otherwise. - */ -uint UNUSED -snmp_varbind_size(const struct agentx_varbind *vb, uint limit) -{ - if (limit < sizeof(struct agentx_varbind)) - return 0; - - if (!snmp_test_varbind_type(vb->type)) - return 0; - - enum agentx_type type = vb->type; - int s = agentx_type_size(type); - uint vb_header = snmp_varbind_header_size(&vb->name); - - if (limit < vb_header) - return 0; - - if (s == 0) - return vb_header; - - if (s > 0 && vb_header + s <= limit) - return vb_header + s; - else if (s > 0) - return 0; - - uint sz; - switch (type) - { - case AGENTX_OBJECT_ID:; - struct oid *oid = snmp_varbind_data(vb); - /* snmp_oid_size works for both native and packet byte order */ - sz = snmp_oid_size(oid); - if (limit < vb_header + sz) - return 0; - else - return vb_header + snmp_oid_size(oid); - - case AGENTX_OCTET_STRING: - case AGENTX_IP_ADDRESS: - case AGENTX_OPAQUE:; - struct agentx_octet_str *os = snmp_varbind_data(vb); - sz = snmp_get_octet_size(os); - if (limit < vb_header + sz) - return 0; - else - return vb_header + sz; - - default: - /* This should not happen */ - return 0; - } -} - /** * snmp_varbind_size_from_len - get size in-buffer VarBind for known OID and data * @n_subid: number of subidentifiers of the VarBind's OID name @@ -429,7 +283,7 @@ snmp_test_varbind_type(u16 type) /** - * snmp_oid_ip4_index - check IPv4 address validity in oid + * snmp_valid_ip4_index - check IPv4 address validity in oid * @o: object identifier holding ip address * @start: index of first address id */ @@ -534,25 +388,6 @@ snmp_put_fbyte(byte *buf, u8 data) return buf + 3; } -/* - * snmp_oid_ip4_index - OID append IPv4 index - * @o: OID to use - * @start: index of IP addr's MSB - * @addr: IPv4 address to use - * - * The indices from start to (inclusive) start+3 are overwritten by @addr bytes. - */ -void -snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr) -{ - u32 temp = ip4_to_u32(addr); - o->ids[start] = temp >> 24; - o->ids[start + 1] = (temp >> 16) & 0xFF; - o->ids[start + 2] = (temp >> 8) & 0xFF; - o->ids[start + 3] = temp & 0xFF; -} - - /** * snmp_oid_compare - find the lexicographical order relation between @left and @right * @left: left object id relation operant @@ -654,8 +489,8 @@ snmp_registration_create(struct snmp_proto *p, enum agentx_mibs mib) r->n.prev = r->n.next = NULL; r->session_id = p->session_id; - /* will be incremented by snmp_session() macro during packet assembly */ r->transaction_id = p->transaction_id; + /* will be incremented by snmp_session() macro during packet assembly */ r->packet_id = p->packet_id + 1; r->mib = mib; @@ -673,15 +508,6 @@ snmp_registration_match(struct snmp_registration *r, struct agentx_header *h) } -void UNUSED -snmp_dump_packet(byte UNUSED *pkt, uint size) -{ - DBG("dump"); - for (uint i = 0; i < size; i += 4) - DBG("pkt [%d] 0x%02x%02x%02x%02x", i, pkt[i],pkt[i+1],pkt[i+2],pkt[i+3]); - DBG("end dump"); -} - /* * agentx_type_size - get in packet VarBind type size * @type: VarBind type @@ -759,18 +585,6 @@ snmp_varbind_ip4(struct snmp_pdu *c, ip4_addr addr) c->buffer = snmp_put_ip4(snmp_varbind_data(c->sr_vb_start), addr); } -#if 0 -inline byte * -snmp_varbind_nstr2(struct snmp_pdu *c, uint size, const char *str, uint len) -{ - if (size < snmp_str_size_from_len(len)) - return NULL; - - c->sr_vb_start = AGENTX_OCTET_STRING; - return snmp_put_nstr(snmp_varbind_data(c->sr_vb_start), str, len); -} -#endif - /* * snmp_varbind_nstr - fill varbind context with octet string * @vb: VarBind to use @@ -894,6 +708,8 @@ snmp_oid_log(const struct oid *oid) * be NULL OID in cases where there is no common subid. The result could be also * viewed as longest common prefix. Note that if both @left and @right are * prefixable but not prefixed the result in @out will also not be prefixed. + * + * This function is used intensively by snmp_test.c. */ void snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out) diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index bde43179..b4010be5 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -4,8 +4,6 @@ #include "subagent.h" #include "mib_tree.h" -uint snmp_pkt_len(const byte *start, const byte *end); - /* * * AgentX Variable Biding (VarBind) utils @@ -27,7 +25,6 @@ int snmp_oid_is_prefixable(const struct oid *oid); uint snmp_oid_size(const struct oid *o); size_t snmp_oid_size_from_len(uint n_subid); void snmp_oid_copy(struct oid *dest, const struct oid *src); -void snmp_oid_copy2(struct oid *dest, const struct oid *src); int snmp_oid_compare(const struct oid *first, const struct oid *second); void snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *result); void snmp_oid_from_buf(struct oid *dest, const struct oid *src); @@ -49,28 +46,27 @@ snmp_oid_is_prefixed(const struct oid *oid) /* type IPv4 */ int snmp_valid_ip4_index(const struct oid *o, uint start); int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start); -void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr); /* * AgentX - Variable Binding (VarBind) manupulation */ uint snmp_varbind_header_size(const struct oid *vb_name); -uint snmp_varbind_size(const struct agentx_varbind *vb, uint limit); uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb); size_t snmp_varbind_size_from_len(uint n_subid, enum agentx_type t, uint len); int snmp_test_varbind_type(u16 type); void *snmp_varbind_data(const struct agentx_varbind *vb); -struct oid *snmp_varbind_set_name_len(struct snmp_pdu *c, struct agentx_varbind **vb, u8 len); -void snmp_varbind_duplicate_hdr(struct snmp_pdu *c, struct agentx_varbind **vb); /* * AgentX - PDU headers, types, contexts */ void snmp_session(const struct snmp_proto *p, struct agentx_header *h); -int snmp_has_context(const struct agentx_header *h); void snmp_pdu_context(struct snmp_pdu *pdu, struct snmp_proto *p, sock *sk); -struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid); -struct oid *snmp_oid_blank(struct snmp_proto *p); + +static inline int +snmp_has_context(const struct agentx_header *h) +{ + return LOAD_U8(h->flags) & AGENTX_NON_DEFAULT_CONTEXT; +} int snmp_test_close_reason(byte value); @@ -105,7 +101,6 @@ byte *snmp_put_fbyte(byte *buf, u8 data); struct snmp_registration *snmp_registration_create(struct snmp_proto *p, enum agentx_mibs mib); int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h); -void snmp_dump_packet(byte *pkt, uint size); void snmp_oid_dump(const struct oid *oid); void snmp_oid_log(const struct oid *oid); diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 4e432458..d94b2a79 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -31,16 +31,127 @@ * */ -static uint parse_response(struct snmp_proto *p, byte *buf); -static void do_response(struct snmp_proto *p, byte *buf); -static uint parse_gets_pdu(struct snmp_proto *p, byte *pkt); -static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c); -static void response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind); -static uint update_packet_size(struct agentx_header *start, byte *end); +void snmp_register_ack(struct snmp_proto *p, struct agentx_response *res); /* standard SNMP internet prefix (.1.3.6.1) */ const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET }; +/* + * update_packet_size - set PDU size + * @start: pointer to PDU data start (excluding header size) + * @end: pointer after the last PDU byte + * + * Return number of bytes in TX buffer (including header size). + */ +static inline uint +update_packet_size(struct agentx_header *start, byte *end) +{ + /* pkt_len */ + uint s = (end - (byte *) start) - AGENTX_HEADER_SIZE; + STORE_U32(start->payload, s); + return AGENTX_HEADER_SIZE + s; +} + +/* + * response_err_ind - update response error and index + * @p: SNMP protocol instance + * @res: response PDU header + * @err: error status + * @ind: index of error, ignored for noAgentXError + * + * Update agentx-Response-PDU header fields res.error and it's res.index. If the + * error is not noError, also set the corrent response PDU payload size. + */ +static inline void +response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind) +{ + STORE_U16(res->error, (u16) err); + if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR) + { + if (p->verbose) + TRACE(D_PACKETS, "SNMP last PDU resulted in error %u", err); + STORE_U16(res->index, ind); + /* Reset VarBindList to null */ + STORE_U32(res->h.payload, + sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); + } + else if (err == AGENTX_RES_GEN_ERROR) + { + if (p->verbose) + TRACE(D_PACKETS, "SNMP last PDU resulted in error genErr"); + STORE_U16(res->index, 0); + /* Reset VarBindList to null */ + STORE_U32(res->h.payload, + sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); + } + else + STORE_U16(res->index, 0); +} + +/* + * snmp_varbind_leave - transform VarBind to packet byte order + * @vb: prepared VarBind in cpu native byte order + */ +void +snmp_varbind_leave(struct agentx_varbind *vb) +{ + STORE_U16(vb->type, vb->type); + + /* Does nothing */ + STORE_U16(vb->reserved, 0); + struct oid *oid = &vb->name; + STORE_U8(oid->n_subid, oid->n_subid); + STORE_U8(oid->prefix, oid->prefix); + STORE_U8(oid->include, oid->include); + STORE_U8(oid->reserved, 0); + + for (u8 i = 0; i < oid->n_subid; i++) + STORE_U32(oid->ids[i], oid->ids[i]); +} + +/* + * snmp_tbuf_reserve - conditionally grow the TX buffer + * @c: transmit PDU context + * @size: size to make available + * + * Return non-zero if the buffer was relocated. + */ +int +snmp_tbuf_reserve(struct snmp_pdu *c, size_t size) +{ + if (size >= c->size) + { + struct snmp_proto *p = c->p; + sock *sk = p->sock; + + int start_diff; + if (c->sr_vb_start != NULL) + start_diff = (char *) c->sr_vb_start - (char *) sk->tbuf; + + sk_set_tbsize(sk, sk->tbsize + 2048); + c->size += 2048; + + if (c->sr_vb_start != NULL) + c->sr_vb_start = (struct agentx_varbind *) (sk->tbuf + start_diff); + + return 1; + } + + return 0; +} + +/* + * refresh_ids - Copy current ids from packet to protocol + * @p: SNMP protocol instance + * @h: PDU header with new transaction_id and packet_id ids. + */ +static inline void +refresh_ids(struct snmp_proto *p, struct agentx_header *h) +{ + p->transaction_id = LOAD_U32(h->transaction_id); + p->packet_id = LOAD_U32(h->packet_id); +} + /* * snmp_header - store packet header into buffer * @h: pointer to created packet header in TX buffer @@ -74,32 +185,217 @@ snmp_blank_header(struct agentx_header *h, enum agentx_pdu_types type) } /* - * snmp_register_ack - handle registration response + * prepare_response - fill buffer with AgentX PDU header * @p: SNMP protocol instance - * @res: header of agentx-Response-PDU + * @c: transmit PDU context to use + * + * Prepare known parts of AgentX packet header into the TX buffer held by @c. */ -void -snmp_register_ack(struct snmp_proto *p, struct agentx_response *res) +static struct agentx_response * +prepare_response(struct snmp_proto *p, struct snmp_pdu *c) { - struct snmp_registration *reg; - WALK_LIST(reg, p->registration_queue) + struct agentx_response *r = (void *) c->buffer; + struct agentx_header *h = &r->h; + + snmp_blank_header(h, AGENTX_RESPONSE_PDU); + snmp_session(p, h); + + /* protocol doesn't care about subagent upTime */ + STORE_U32(r->uptime, 0); + STORE_U16(r->error, AGENTX_RES_NO_ERROR); + STORE_U16(r->index, 0); + + ADVANCE(c->buffer, c->size, sizeof(struct agentx_response)); + return r; +} + +/* + * snmp_oid_prefixize_unsafe - normalize OID to prefixed form + * @dest: destination for normalized OID in native byte order + * @src: source OID in packet byte order + * + * Note that again, snmp_oid_prefixize_unsafe is intended to copy Object + * Identifier from RX buffer to TX buffer but also optionally swap the byte + * order from packet b.o. to cpu native b.o. This is done to simplify the code + * dealing with OIDs. + */ +static inline void +snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src) +{ + dest->n_subid = LOAD_U8(src->n_subid) - 5; + dest->prefix = (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)]); + dest->include = (LOAD_U8(src->include)) ? 1 : 0; + dest->reserved = 0; + + /* The LOAD_U32() and STORE_U32() cancel out */ + for (u8 i = 0; i < dest->n_subid; i++) + dest->ids[i] = LOAD_U32(src->ids[i + 5]); +} + +/* + * snmp_vb_to_tx - create VarBind in TX buffer from RX buffer OID + * @c: PDU context + * @oid: Object Identifier located in RX buffer with packet byte order + * + * Create a NULL initialized VarBind inside TX buffer (from @c) whose name + * is @oid. Because we want to simplify code dealing with OIDs, the byte order + * of the name is optionally swapped to match cpu native byte order. + */ +struct agentx_varbind * +snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid) +{ + uint vb_hdr_size = snmp_varbind_header_size(oid); + (void) snmp_tbuf_reserve(c, vb_hdr_size); + + ASSERT(c->size >= vb_hdr_size); + struct agentx_varbind *vb = (struct agentx_varbind *) c->buffer; + ADVANCE(c->buffer, c->size, sizeof(struct agentx_varbind) - sizeof(struct oid)); + /* Move the c->buffer so that is points at &vb->name */ + vb->type = AGENTX_NULL; + + if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid)) { - if (snmp_registration_match(reg, &res->h)) - { - rem_node(®->n); + u8 subids = LOAD_U8(oid->n_subid) - 5; + ADVANCE(c->buffer, c->size, snmp_oid_size_from_len(subids)); + snmp_oid_prefixize_unsafe(&vb->name, oid); - if (res->error == AGENTX_RES_NO_ERROR && reg->reg_hook_ok) - reg->reg_hook_ok(p, res, reg); - else if (res->error != AGENTX_RES_NO_ERROR && reg->reg_hook_fail) - reg->reg_hook_fail(p, res, reg); - - mb_free(reg); - break; - } + return vb; } - if (EMPTY_LIST(p->registration_queue)) - snmp_up(p); + ADVANCE(c->buffer, c->size, snmp_oid_size(oid)); + snmp_oid_from_buf(&vb->name, oid); + + return vb; +} + +// TODO XXX docstring +int +snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c) +{ + byte *pkt = *pkt_ptr; + uint pkt_size = *pkt_sz; + + uint sz; + /* in packet byte order */ + const struct oid *start_buf = (const struct oid *) pkt; + if ((sz = snmp_oid_size(start_buf)) > pkt_size || + LOAD_U8(start_buf->n_subid) >= OID_MAX_LEN) + { + c->error = AGENTX_RES_PARSE_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return 0; + } + + ADVANCE(pkt, pkt_size, sz); + + /* in packet byte order */ + const struct oid *end_buf = (const struct oid *) pkt; + if ((sz = snmp_oid_size(end_buf)) > pkt_size || + LOAD_U8(end_buf->n_subid) >= OID_MAX_LEN) + { + c->error = AGENTX_RES_PARSE_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return 0; + } + + /* in cpu native byte order */ + struct agentx_varbind *start_vb = snmp_vb_to_tx(c, start_buf); + + /* in cpu native byte order */ + struct oid *end_oid = tmp_alloc(sz); + snmp_oid_from_buf(end_oid, end_buf); + + ADVANCE(pkt, pkt_size, sz); + + if (!snmp_is_oid_empty(end_oid) && + snmp_oid_compare(&start_vb->name, end_oid) > 0) + { + c->error = AGENTX_RES_GEN_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return 0; + } + + ASSERT(start_vb != NULL); + ASSERT(end_oid != NULL); + + c->sr_vb_start = start_vb; + c->sr_o_end = end_oid; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return 1; /* ok */ +} + +/* + * close_pdu - send an agentx-Close-PDU + * @p: SNMP protocol instance + * @reason: reason for closure + */ +static void +close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason) +{ + sock *sk = p->sock; + struct snmp_pdu c; + snmp_pdu_context(&c, p, sk); + + TRACE(D_PACKETS, "SNMP sending agentx-Close-PDU with reason %u", reason); + +#define REASON_SIZE sizeof(u32) + (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE + REASON_SIZE); + + struct agentx_header *h = (void *) c.buffer; + ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); + snmp_blank_header(h, AGENTX_CLOSE_PDU); + p->packet_id++; + snmp_session(p, h); + + (void) snmp_put_fbyte(c.buffer, (u8) reason); + ADVANCE(c.buffer, c.size, REASON_SIZE); + + uint s = update_packet_size(h, c.buffer); + sk_send(sk, s); +#undef REASON_SIZE +} + +/* + * ping_pdu - send an agentx-Ping-PDU + * @p: SNMP protocol instance + */ +void +snmp_ping(struct snmp_proto *p) +{ + /* ping_pdu */ + if (!snmp_is_active(p)) + return; + + sock *sk = p->sock; + struct snmp_pdu c; + snmp_pdu_context(&c, p, sk); + + if (c.size < AGENTX_HEADER_SIZE) + return; + + int unused = sk->tbuf + sk->tbsize - c.buffer; + if (unused < AGENTX_HEADER_SIZE) + return; + + struct agentx_header *h = (void *) c.buffer; + ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); + snmp_blank_header(h, AGENTX_PING_PDU); + p->packet_id++; + snmp_session(p, h); + if (p->verbose) + TRACE(D_PACKETS, "SNMP sending agentx-Ping-PDU"); + p->ignore_ping_id = p->packet_id; + + /* sending only header */ + uint s = update_packet_size(h, (byte *) h + AGENTX_HEADER_SIZE); + + if (p->packet_id) + p->pings++; + sk_send(sk, s); } /* @@ -125,62 +421,6 @@ snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16 sk_send(sk, sizeof(struct agentx_response)); } -/* - * open_pdu - send an agentx-Open-PDU - * @p: SNMP protocol instance - * @oid: PDU OID description field value - * - * Other fields are filled based on @p configuration (timeout, subagent - * description). - */ -static void -open_pdu(struct snmp_proto *p, struct oid *oid) -{ - const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf); - sock *sk = p->sock; - - TRACE(D_PACKETS, "SNMP sending agentx-Open-PDU"); - - struct snmp_pdu c; - snmp_pdu_context(&c, p, sk); - -#define TIMEOUT_SIZE sizeof(u32) /* 1B timeout, 3B zero padding */ - - /* Make sure that we have enough space in TX buffer */ - uint s = AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) + - snmp_str_size(cf->description); - - (void) snmp_tbuf_reserve(&c, s); - - struct agentx_header *h = (void *) c.buffer; - ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); - snmp_blank_header(h, AGENTX_OPEN_PDU); - - STORE_U32(h->session_id, 1); - STORE_U32(h->transaction_id, 1); - STORE_U32(h->packet_id, 1); - - c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description)); - - if (p->timeout >= 1 S && p->timeout <= 255 S) - /* 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); - - snmp_oid_to_buf((struct oid *) c.buffer, oid); - c.buffer += snmp_oid_size(oid); - c.buffer = snmp_put_str(c.buffer, cf->description); - - s = update_packet_size(h, c.buffer); - sk_send(sk, s); -#undef TIMEOUT_SIZE -} - /* * send_notify_pdu - send an agentx-Notify-PDU * @p: SNMP protocol instance @@ -343,6 +583,22 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, en #undef BOUND_SIZE } +/* + * snmp_unregister - send an agentx-Unregister-PDU + * @p: SNMP protocol instance + * @oid: OID to uregister + * @bound: OIDs unregistration upper bound + * @index: OIDs unregistration n_subid index + * + * For more detailed description see un_register_pdu() function. + */ +void UNUSED +snmp_unregister(struct snmp_proto *p, struct oid *oid, u32 bound, uint index) +{ + TRACE(D_PACKETS, "SNMP sending agentx-Unregister-PDU"); + un_register_pdu(p, oid, bound, index, AGENTX_UNREGISTER_PDU, 0); +} + /* * snmp_register - send an agentx-Register-PDU * @p: SNMP protocol instance @@ -361,50 +617,59 @@ snmp_register(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, u8 i } /* - * snmp_unregister - send an agentx-Unregister-PDU + * open_pdu - send an agentx-Open-PDU * @p: SNMP protocol instance - * @oid: OID to uregister - * @bound: OIDs unregistration upper bound - * @index: OIDs unregistration n_subid index + * @oid: PDU OID description field value * - * For more detailed description see un_register_pdu() function. - */ -void UNUSED -snmp_unregister(struct snmp_proto *p, struct oid *oid, u32 bound, uint index) -{ - TRACE(D_PACKETS, "SNMP sending agentx-Unregister-PDU"); - un_register_pdu(p, oid, bound, index, AGENTX_UNREGISTER_PDU, 0); -} - -/* - * close_pdu - send an agentx-Close-PDU - * @p: SNMP protocol instance - * @reason: reason for closure + * Other fields are filled based on @p configuration (timeout, subagent + * description). */ static void -close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason) +open_pdu(struct snmp_proto *p, struct oid *oid) { + const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf); sock *sk = p->sock; + + TRACE(D_PACKETS, "SNMP sending agentx-Open-PDU"); + struct snmp_pdu c; snmp_pdu_context(&c, p, sk); - TRACE(D_PACKETS, "SNMP sending agentx-Close-PDU with reason %u", reason); +#define TIMEOUT_SIZE sizeof(u32) /* 1B timeout, 3B zero padding */ -#define REASON_SIZE sizeof(u32) - (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE + REASON_SIZE); + /* Make sure that we have enough space in TX buffer */ + uint s = AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) + + snmp_str_size(cf->description); + + (void) snmp_tbuf_reserve(&c, s); struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); - snmp_blank_header(h, AGENTX_CLOSE_PDU); - p->packet_id++; - snmp_session(p, h); + snmp_blank_header(h, AGENTX_OPEN_PDU); - (void) snmp_put_fbyte(c.buffer, (u8) reason); - ADVANCE(c.buffer, c.size, REASON_SIZE); + STORE_U32(h->session_id, 1); + STORE_U32(h->transaction_id, 1); + STORE_U32(h->packet_id, 1); - uint s = update_packet_size(h, c.buffer); + c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description)); + + if (p->timeout >= 1 S && p->timeout <= 255 S) + /* 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); + + snmp_oid_to_buf((struct oid *) c.buffer, oid); + c.buffer += snmp_oid_size(oid); + c.buffer = snmp_put_str(c.buffer, cf->description); + + s = update_packet_size(h, c.buffer); sk_send(sk, s); -#undef REASON_SIZE +#undef TIMEOUT_SIZE } /* @@ -446,17 +711,117 @@ parse_close_pdu(struct snmp_proto *p, byte * const pkt_start) return pkt_size + AGENTX_HEADER_SIZE; } +/* + * parse_sets_pdu - common functionality for commit set and undo set PDUs + * @p: SNMP protocol instance + * @pkt_start: pointer to first byte of on of set related PDU + * @error: error status to use + * + * Return number of bytes parsed from RX buffer. + */ +static uint +parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_response_errs err) +{ + byte *pkt = pkt_start; + /* Presence of full header is guaranteed by parse_pkt() caller */ + struct agentx_header *h = (void *) pkt; + pkt += AGENTX_HEADER_SIZE; + uint pkt_size = LOAD_U32(h->payload); + + if (pkt_size != 0) + { + TRACE(D_PACKETS, "SNMP received PDU is malformed (size)"); + snmp_simple_response(p, AGENTX_RES_PARSE_ERROR, 0); + snmp_reset(p); + return 0; + } + + struct snmp_pdu c; + snmp_pdu_context(&c, p, p->sock); + (void) snmp_tbuf_reserve(&c, sizeof(struct agentx_response)); + + struct agentx_response *r = prepare_response(p, &c); + + // TODO free resource allocated by parse_test_set_pdu() + // TODO do something meaningful + //mb_free(tr); + c.error = err; + + TRACE(D_PACKETS, "SNMP received PDU parsed with error %u", c.error); + response_err_ind(p, r, c.error, 0); + sk_send(p->sock, AGENTX_HEADER_SIZE); + + /* Reset the connection on unrecoverable error */ + if (c.error != AGENTX_RES_NO_ERROR && c.error != err) + { + snmp_reset(p); /* error */ + return 0; + } + + return pkt - pkt_start; +} /* - * refresh_ids - Copy current ids from packet to protocol + * parse_cleanup_set_pdu - parse an agentx-CleanupSet-PDU * @p: SNMP protocol instance - * @h: PDU header with new transaction_id and packet_id ids. + * @pkt_start: pointer to first byte of PDU inside RX buffer + * + * Return number of bytes parsed from RX buffer. */ -static inline void -refresh_ids(struct snmp_proto *p, struct agentx_header *h) +static uint +parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start) { - p->transaction_id = LOAD_U32(h->transaction_id); - p->packet_id = LOAD_U32(h->packet_id); + byte *pkt = pkt_start; + struct agentx_header *h = (void *) pkt; + uint pkt_size = LOAD_U32(h->payload); + + /* errors are dropped silently, we must not send any agentx-Response-PDU */ + if (pkt_size != 0) + { + return AGENTX_HEADER_SIZE; + TRACE(D_PACKETS, "SNMP received agentx-CleanupSet-PDU is malformed"); + snmp_reset(p); + return 0; + } + + TRACE(D_PACKETS, "SNMP received agentx-CleanupSet-PDU"); + (void)p; + // TODO don't forget to free resources allocated by parse_test_set_pdu() + //mb_free(p->tr); + /* No agentx-Response-PDU is sent in response to agentx-CleanupSet-PDU */ + return pkt_size; +} + +/* + * parse_undo_set_pdu - parse an agentx-UndoSet-PDU + * @p: SNMP protocol instance + * @pkt: pointer to first byte of PDU inside RX buffer + * + * Return number of bytes parsed from buffer. + */ +static inline uint +parse_undo_set_pdu(struct snmp_proto *p, byte *pkt) +{ + // 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_sets_pdu(p, pkt, AGENTX_RES_UNDO_FAILED); +} + +/* + * parse_commit_set_pdu - parse an agentx-CommitSet-PDU + * @p: SNMP protocol instance + * @pkt: pointer to first byte of PDU inside RX buffer + * + * Return number of bytes parsed from RX buffer. + */ +static inline uint +parse_commit_set_pdu(struct snmp_proto *p, byte *pkt) +{ + // 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_sets_pdu(p, pkt, AGENTX_RES_COMMIT_FAILED); } /* @@ -517,507 +882,6 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start) return pkt - pkt_start; } -/* - * parse_sets_pdu - common functionality for commit set and undo set PDUs - * @p: SNMP protocol instance - * @pkt_start: pointer to first byte of on of set related PDU - * @error: error status to use - * - * Return number of bytes parsed from RX buffer. - */ -static uint -parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_response_errs err) -{ - byte *pkt = pkt_start; - /* Presence of full header is guaranteed by parse_pkt() caller */ - struct agentx_header *h = (void *) pkt; - pkt += AGENTX_HEADER_SIZE; - uint pkt_size = LOAD_U32(h->payload); - - if (pkt_size != 0) - { - TRACE(D_PACKETS, "SNMP received PDU is malformed (size)"); - snmp_simple_response(p, AGENTX_RES_PARSE_ERROR, 0); - snmp_reset(p); - return 0; - } - - struct snmp_pdu c; - snmp_pdu_context(&c, p, p->sock); - (void) snmp_tbuf_reserve(&c, sizeof(struct agentx_response)); - - struct agentx_response *r = prepare_response(p, &c); - - // TODO free resource allocated by parse_test_set_pdu() - // TODO do something meaningful - //mb_free(tr); - c.error = err; - - TRACE(D_PACKETS, "SNMP received PDU parsed with error %u", c.error); - response_err_ind(p, r, c.error, 0); - sk_send(p->sock, AGENTX_HEADER_SIZE); - - /* Reset the connection on unrecoverable error */ - if (c.error != AGENTX_RES_NO_ERROR && c.error != err) - { - snmp_reset(p); /* error */ - return 0; - } - - return pkt - pkt_start; -} - -/* - * parse_commit_set_pdu - parse an agentx-CommitSet-PDU - * @p: SNMP protocol instance - * @pkt: pointer to first byte of PDU inside RX buffer - * - * Return number of bytes parsed from RX buffer. - */ -static inline uint -parse_commit_set_pdu(struct snmp_proto *p, byte *pkt) -{ - // 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_sets_pdu(p, pkt, AGENTX_RES_COMMIT_FAILED); -} - -/* - * parse_undo_set_pdu - parse an agentx-UndoSet-PDU - * @p: SNMP protocol instance - * @pkt: pointer to first byte of PDU inside RX buffer - * - * Return number of bytes parsed from buffer. - */ -static inline uint -parse_undo_set_pdu(struct snmp_proto *p, byte *pkt) -{ - // 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_sets_pdu(p, pkt, AGENTX_RES_UNDO_FAILED); -} - -/* - * parse_cleanup_set_pdu - parse an agentx-CleanupSet-PDU - * @p: SNMP protocol instance - * @pkt_start: pointer to first byte of PDU inside RX buffer - * - * Return number of bytes parsed from RX buffer. - */ -static uint -parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start) -{ - byte *pkt = pkt_start; - struct agentx_header *h = (void *) pkt; - uint pkt_size = LOAD_U32(h->payload); - - /* errors are dropped silently, we must not send any agentx-Response-PDU */ - if (pkt_size != 0) - { - return AGENTX_HEADER_SIZE; - TRACE(D_PACKETS, "SNMP received agentx-CleanupSet-PDU is malformed"); - snmp_reset(p); - return 0; - } - - TRACE(D_PACKETS, "SNMP received agentx-CleanupSet-PDU"); - (void)p; - // TODO don't forget to free resources allocated by parse_test_set_pdu() - //mb_free(p->tr); - /* No agentx-Response-PDU is sent in response to agentx-CleanupSet-PDU */ - return pkt_size; -} - -/* - * space_for_response - check if TX buffer has space for agentx-Response-PDU - * @sk: communication socket owned by SNMP protocol instance - * - * In some cases we send only the AgentX header but if we want to signal an - * error, we need at least space for agentx-Response-PDU. This simplifies the - * PDU space requirements testing. - */ -static inline int -space_for_response(const sock *sk) -{ - return ( - (uint) (sk->tbuf + sk->tbsize - sk->tpos) >= sizeof(struct agentx_response) - ); -} - -/** - * parse_pkt - parse received AgentX packet - * @p: SNMP protocol instance - * @pkt: first byte of PDU inside RX buffer - * @size: number of bytes received from a socket - * - * Return number of bytes parsed from RX buffer. - */ -static uint -parse_pkt(struct snmp_proto *p, byte *pkt, uint size) -{ - if (size < AGENTX_HEADER_SIZE) - return 0; - - struct agentx_header *h = (struct agentx_header *) pkt; - if (h->flags & AGENTX_NETWORK_BYTE_ORDER != SNMP_BYTE_ORDER) - { - TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order"); - if (h->type != AGENTX_RESPONSE_PDU) - snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); - snmp_reset(p); - return 0; - } - - u32 pkt_size = LOAD_U32(h->payload); - - /* RX side checks - too big packet */ - if (pkt_size > SNMP_PKT_SIZE_MAX) - { - TRACE(D_PACKETS, "SNMP received PDU is too long"); - if (h->type != AGENTX_RESPONSE_PDU) - snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); - snmp_reset(p); - return 0; - } - - /* This guarantees that we have the full packet already received */ - if (size < pkt_size + AGENTX_HEADER_SIZE) - return 0; /* no bytes parsed */ - - /* - * We need to see the responses for PDU such as - * agentx-Open-PDU, agentx-Register-PDU, ... - * even when we are outside the SNMP_CONNECTED state - */ - if (h->type == AGENTX_RESPONSE_PDU) - return parse_response(p, pkt); - - ASSERT(snmp_is_active(p)); - if (p->state != SNMP_CONN || - p->session_id != LOAD_U32(h->session_id)) - { - struct agentx_header copy = { - .session_id = p->session_id, - .transaction_id = p->transaction_id, - .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; - p->transaction_id = copy.transaction_id; - p->packet_id = copy.packet_id; - - /* - * After unexpected state, we simply reset the session - * only sending the agentx-Response-PDU. - */ - snmp_reset(p); - return 0; - } - - if (h->flags & AGENTX_NON_DEFAULT_CONTEXT) - { - TRACE(D_PACKETS, "SNMP received PDU with non-default context"); - snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0); - snmp_reset(p); - return 0; - } - - refresh_ids(p, h); - switch (LOAD_U8(h->type)) - { - case AGENTX_GET_PDU: - TRACE(D_PACKETS, "SNMP received agentx-Get-PDU"); - return parse_gets_pdu(p, pkt); - - case AGENTX_GET_NEXT_PDU: - TRACE(D_PACKETS, "SNMP received agentx-GetNext-PDU"); - return parse_gets_pdu(p, pkt); - - case AGENTX_GET_BULK_PDU: - TRACE(D_PACKETS, "SNMP received agentx-GetBulk-PDU"); - return parse_gets_pdu(p, pkt); - - case AGENTX_CLOSE_PDU: - return parse_close_pdu(p, pkt); - - case AGENTX_TEST_SET_PDU: - return parse_test_set_pdu(p, pkt); - - case AGENTX_COMMIT_SET_PDU: - return parse_commit_set_pdu(p, pkt); - - case AGENTX_UNDO_SET_PDU: - return parse_undo_set_pdu(p, pkt); - - case AGENTX_CLEANUP_SET_PDU: - return parse_cleanup_set_pdu(p, pkt); - - default: - /* We reset the connection for malformed packet (Unknown packet type) */ - TRACE(D_PACKETS, "SNMP received PDU with unknown type (%u)", LOAD_U8(h->type)); - snmp_reset(p); - return 0; - } -} - - -/* - * parse_response - parse an agentx-Response-PDU - * @p: SNMP protocol instance - * @res: pointer of agentx-Response-PDU header in RX buffer - * - * Return number of bytes parsed from RX buffer. - */ -static uint -parse_response(struct snmp_proto *p, byte *res) -{ - struct agentx_response *r = (void *) res; - struct agentx_header *h = (void *) r; - - uint pkt_size = LOAD_U32(h->payload); - - if (p->ignore_ping_id && LOAD_U32(h->packet_id) == p->ignore_ping_id) - { - p->pings--; - p->ignore_ping_id = 0; - } - - /* Number of agentx-Ping-PDU without response */ - if (p->pings > 5) - snmp_reset(p); - - switch (r->error) - { - case AGENTX_RES_NO_ERROR: - if (p->verbose || LOAD_U32(h->packet_id) != p->ignore_ping_id) - TRACE(D_PACKETS, "SNMP received agentx-Response-PDU"); - do_response(p, res); - break; - - /* Registration errors */ - case AGENTX_RES_DUPLICATE_REGISTER: - case AGENTX_RES_REQUEST_DENIED: - case AGENTX_RES_UNKNOWN_REGISTER: - TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error); - snmp_register_ack(p, r); - break; - - /* - * 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: /* currently we don't use contexts */ - case AGENTX_RES_PARSE_ERROR: - case AGENTX_RES_PROCESSING_ERR: - default: - TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with unexepected error %u", r->error); - snmp_reset(p); - break; - } - - return pkt_size + AGENTX_HEADER_SIZE; -} - -/* - * snmp_register_mibs - register all MIB subtrees - * @p: SNMP protocol instance - */ -void -snmp_register_mibs(struct snmp_proto *p) -{ - snmp_bgp4_register(p); - ASSUME(!EMPTY_LIST(p->registration_queue)); -} - -/* - * do_response - act on agentx-Response-PDU and protocol state - * @p: SNMP protocol instance - * @pkt: RX buffer with PDU bytes - * - * Return number of bytes parsed from RX buffer. - */ -static void -do_response(struct snmp_proto *p, byte *pkt) -{ - struct agentx_response *r = (void *) pkt; - struct agentx_header *h = (void *) r; - - switch (p->state) - { - case SNMP_INIT: - case SNMP_LOCKED: - /* silent drop of received packet */ - break; - - case SNMP_OPEN: - /* copy session info from received packet */ - 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_set_state(p, SNMP_REGISTER); - break; - - case SNMP_REGISTER:; - snmp_register_ack(p, r); - break; - - case SNMP_CONN: - break; - - case SNMP_STOP: - case SNMP_DOWN: - break; - - default: - die("unkonwn SNMP state"); - } -} - -/* - * snmp_oid_prefixize_unsafe - normalize OID to prefixed form - * @dest: destination for normalized OID in native byte order - * @src: source OID in packet byte order - * - * Note that again, snmp_oid_prefixize_unsafe is intended to copy Object - * Identifier from RX buffer to TX buffer but also optionally swap the byte - * order from packet b.o. to cpu native b.o. This is done to simplify the code - * dealing with OIDs. - */ -static inline void -snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src) -{ - dest->n_subid = LOAD_U8(src->n_subid) - 5; - dest->prefix = (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)]); - dest->include = (LOAD_U8(src->include)) ? 1 : 0; - dest->reserved = 0; - - /* The LOAD_U32() and STORE_U32() cancel out */ - for (u8 i = 0; i < dest->n_subid; i++) - dest->ids[i] = LOAD_U32(src->ids[i + 5]); -} - -/* - * snmp_vb_to_tx - create VarBind in TX buffer from RX buffer OID - * @c: PDU context - * @oid: Object Identifier located in RX buffer with packet byte order - * - * Create a NULL initialized VarBind inside TX buffer (from @c) whose name - * is @oid. Because we want to simplify code dealing with OIDs, the byte order - * of the name is optionally swapped to match cpu native byte order. - */ -struct agentx_varbind * -snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid) -{ - uint vb_hdr_size = snmp_varbind_header_size(oid); - (void) snmp_tbuf_reserve(c, vb_hdr_size); - - ASSERT(c->size >= vb_hdr_size); - struct agentx_varbind *vb = (struct agentx_varbind *) c->buffer; - ADVANCE(c->buffer, c->size, sizeof(struct agentx_varbind) - sizeof(struct oid)); - /* Move the c->buffer so that is points at &vb->name */ - vb->type = AGENTX_NULL; - - if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid)) - { - u8 subids = LOAD_U8(oid->n_subid) - 5; - ADVANCE(c->buffer, c->size, snmp_oid_size_from_len(subids)); - snmp_oid_prefixize_unsafe(&vb->name, oid); - - return vb; - } - - ADVANCE(c->buffer, c->size, snmp_oid_size(oid)); - snmp_oid_from_buf(&vb->name, oid); - - return vb; -} - -/* - * snmp_varbind_leave - transform VarBind to packet byte order - * @vb: prepared VarBind in cpu native byte order - */ -void -snmp_varbind_leave(struct agentx_varbind *vb) -{ - STORE_U16(vb->type, vb->type); - - /* Does nothing */ - STORE_U16(vb->reserved, 0); - struct oid *oid = &vb->name; - STORE_U8(oid->n_subid, oid->n_subid); - STORE_U8(oid->prefix, oid->prefix); - STORE_U8(oid->include, oid->include); - STORE_U8(oid->reserved, 0); - - for (u8 i = 0; i < oid->n_subid; i++) - STORE_U32(oid->ids[i], oid->ids[i]); -} - -/* - * update_packet_size - set PDU size - * @start: pointer to PDU data start (excluding header size) - * @end: pointer after the last PDU byte - * - * Return number of bytes in TX buffer (including header size). - */ -static inline uint -update_packet_size(struct agentx_header *start, byte *end) -{ - uint s = snmp_pkt_len((byte *) start, end); - STORE_U32(start->payload, s); - return AGENTX_HEADER_SIZE + s; -} - -/* - * response_err_ind - update response error and index - * @p: SNMP protocol instance - * @res: response PDU header - * @err: error status - * @ind: index of error, ignored for noAgentXError - * - * Update agentx-Response-PDU header fields res.error and it's res.index. If the - * error is not noError, also set the corrent response PDU payload size. - */ -static inline void -response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind) -{ - STORE_U16(res->error, (u16) err); - if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR) - { - if (p->verbose) - TRACE(D_PACKETS, "SNMP last PDU resulted in error %u", err); - STORE_U16(res->index, ind); - /* Reset VarBindList to null */ - STORE_U32(res->h.payload, - sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); - } - else if (err == AGENTX_RES_GEN_ERROR) - { - if (p->verbose) - TRACE(D_PACKETS, "SNMP last PDU resulted in error genErr"); - STORE_U16(res->index, 0); - /* Reset VarBindList to null */ - STORE_U32(res->h.payload, - sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); - } - else - STORE_U16(res->index, 0); -} - /* * AgentX GetPDU, GetNextPDU and GetBulkPDU */ @@ -1068,65 +932,6 @@ snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_stat //bulk->has_any |= snmp_get_next_pdu(p, c, walk); } -int -snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c) -{ - byte *pkt = *pkt_ptr; - uint pkt_size = *pkt_sz; - - uint sz; - /* in packet byte order */ - const struct oid *start_buf = (const struct oid *) pkt; - if ((sz = snmp_oid_size(start_buf)) > pkt_size || - LOAD_U8(start_buf->n_subid) >= OID_MAX_LEN) - { - c->error = AGENTX_RES_PARSE_ERROR; - *pkt_ptr = pkt; - *pkt_sz = pkt_size; - return 0; - } - - ADVANCE(pkt, pkt_size, sz); - - /* in packet byte order */ - const struct oid *end_buf = (const struct oid *) pkt; - if ((sz = snmp_oid_size(end_buf)) > pkt_size || - LOAD_U8(end_buf->n_subid) >= OID_MAX_LEN) - { - c->error = AGENTX_RES_PARSE_ERROR; - *pkt_ptr = pkt; - *pkt_sz = pkt_size; - return 0; - } - - /* in cpu native byte order */ - struct agentx_varbind *start_vb = snmp_vb_to_tx(c, start_buf); - - /* in cpu native byte order */ - struct oid *end_oid = tmp_alloc(sz); - snmp_oid_from_buf(end_oid, end_buf); - - ADVANCE(pkt, pkt_size, sz); - - if (!snmp_is_oid_empty(end_oid) && - snmp_oid_compare(&start_vb->name, end_oid) > 0) - { - c->error = AGENTX_RES_GEN_ERROR; - *pkt_ptr = pkt; - *pkt_sz = pkt_size; - return 0; - } - - ASSERT(start_vb != NULL); - ASSERT(end_oid != NULL); - - c->sr_vb_start = start_vb; - c->sr_o_end = end_oid; - *pkt_ptr = pkt; - *pkt_sz = pkt_size; - return 1; /* ok */ -} - /* * parse_gets_pdu - parse received gets PDUs * @p: SNMP protocol instance @@ -1250,19 +1055,261 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) } /* - * snmp_start_subagent - send session open request + * do_response - act on agentx-Response-PDU and protocol state * @p: SNMP protocol instance + * @pkt: RX buffer with PDU bytes * - * Send agentx-Open-PDU with configured OID and string description. + * Return number of bytes parsed from RX buffer. + */ +static void +do_response(struct snmp_proto *p, byte *pkt) +{ + struct agentx_response *r = (void *) pkt; + struct agentx_header *h = (void *) r; + + switch (p->state) + { + case SNMP_INIT: + case SNMP_LOCKED: + /* silent drop of received packet */ + break; + + case SNMP_OPEN: + /* copy session info from received packet */ + 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_set_state(p, SNMP_REGISTER); + break; + + case SNMP_REGISTER:; + snmp_register_ack(p, r); + break; + + case SNMP_CONN: + break; + + case SNMP_STOP: + case SNMP_DOWN: + break; + + default: + die("unkonwn SNMP state"); + } +} + +/* + * parse_response - parse an agentx-Response-PDU + * @p: SNMP protocol instance + * @res: pointer of agentx-Response-PDU header in RX buffer + * + * Return number of bytes parsed from RX buffer. + */ +static uint +parse_response(struct snmp_proto *p, byte *res) +{ + struct agentx_response *r = (void *) res; + struct agentx_header *h = (void *) r; + + uint pkt_size = LOAD_U32(h->payload); + + if (p->ignore_ping_id && LOAD_U32(h->packet_id) == p->ignore_ping_id) + { + p->pings--; + p->ignore_ping_id = 0; + } + + /* Number of agentx-Ping-PDU without response */ + if (p->pings > 5) + snmp_reset(p); + + switch (r->error) + { + case AGENTX_RES_NO_ERROR: + if (p->verbose || LOAD_U32(h->packet_id) != p->ignore_ping_id) + TRACE(D_PACKETS, "SNMP received agentx-Response-PDU"); + do_response(p, res); + break; + + /* Registration errors */ + case AGENTX_RES_DUPLICATE_REGISTER: + case AGENTX_RES_REQUEST_DENIED: + case AGENTX_RES_UNKNOWN_REGISTER: + TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error); + snmp_register_ack(p, r); + break; + + /* + * 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: /* currently we don't use contexts */ + case AGENTX_RES_PARSE_ERROR: + case AGENTX_RES_PROCESSING_ERR: + default: + TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with unexepected error %u", r->error); + snmp_reset(p); + break; + } + + return pkt_size + AGENTX_HEADER_SIZE; +} + +/** + * parse_pkt - parse received AgentX packet + * @p: SNMP protocol instance + * @pkt: first byte of PDU inside RX buffer + * @size: number of bytes received from a socket + * + * Return number of bytes parsed from RX buffer. + */ +static uint +parse_pkt(struct snmp_proto *p, byte *pkt, uint size) +{ + if (size < AGENTX_HEADER_SIZE) + return 0; + + struct agentx_header *h = (struct agentx_header *) pkt; + if (h->flags & AGENTX_NETWORK_BYTE_ORDER != SNMP_BYTE_ORDER) + { + TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order"); + if (h->type != AGENTX_RESPONSE_PDU) + snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); + snmp_reset(p); + return 0; + } + + u32 pkt_size = LOAD_U32(h->payload); + + /* RX side checks - too big packet */ + if (pkt_size > SNMP_PKT_SIZE_MAX) + { + TRACE(D_PACKETS, "SNMP received PDU is too long"); + if (h->type != AGENTX_RESPONSE_PDU) + snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); + snmp_reset(p); + return 0; + } + + /* This guarantees that we have the full packet already received */ + if (size < pkt_size + AGENTX_HEADER_SIZE) + return 0; /* no bytes parsed */ + + /* + * We need to see the responses for PDU such as + * agentx-Open-PDU, agentx-Register-PDU, ... + * even when we are outside the SNMP_CONNECTED state + */ + if (h->type == AGENTX_RESPONSE_PDU) + return parse_response(p, pkt); + + ASSERT(snmp_is_active(p)); + if (p->state != SNMP_CONN || + p->session_id != LOAD_U32(h->session_id)) + { + struct agentx_header copy = { + .session_id = p->session_id, + .transaction_id = p->transaction_id, + .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; + p->transaction_id = copy.transaction_id; + p->packet_id = copy.packet_id; + + /* + * After unexpected state, we simply reset the session + * only sending the agentx-Response-PDU. + */ + snmp_reset(p); + return 0; + } + + if (h->flags & AGENTX_NON_DEFAULT_CONTEXT) + { + TRACE(D_PACKETS, "SNMP received PDU with non-default context"); + snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0); + snmp_reset(p); + return 0; + } + + refresh_ids(p, h); + switch (LOAD_U8(h->type)) + { + case AGENTX_GET_PDU: + TRACE(D_PACKETS, "SNMP received agentx-Get-PDU"); + return parse_gets_pdu(p, pkt); + + case AGENTX_GET_NEXT_PDU: + TRACE(D_PACKETS, "SNMP received agentx-GetNext-PDU"); + return parse_gets_pdu(p, pkt); + + case AGENTX_GET_BULK_PDU: + TRACE(D_PACKETS, "SNMP received agentx-GetBulk-PDU"); + return parse_gets_pdu(p, pkt); + + case AGENTX_CLOSE_PDU: + return parse_close_pdu(p, pkt); + + case AGENTX_TEST_SET_PDU: + return parse_test_set_pdu(p, pkt); + + case AGENTX_COMMIT_SET_PDU: + return parse_commit_set_pdu(p, pkt); + + case AGENTX_UNDO_SET_PDU: + return parse_undo_set_pdu(p, pkt); + + case AGENTX_CLEANUP_SET_PDU: + return parse_cleanup_set_pdu(p, pkt); + + default: + /* We reset the connection for malformed packet (Unknown packet type) */ + TRACE(D_PACKETS, "SNMP received PDU with unknown type (%u)", LOAD_U8(h->type)); + snmp_reset(p); + return 0; + } +} + +/* + * snmp_register_ack - handle registration response + * @p: SNMP protocol instance + * @res: header of agentx-Response-PDU */ void -snmp_start_subagent(struct snmp_proto *p) +snmp_register_ack(struct snmp_proto *p, struct agentx_response *res) { - ASSUME(p->state == SNMP_OPEN); + struct snmp_registration *reg; + WALK_LIST(reg, p->registration_queue) + { + if (snmp_registration_match(reg, &res->h)) + { + rem_node(®->n); - /* blank oid means unsupported */ - STATIC_OID(0) blank = { 0 }; - open_pdu(p, (struct oid *) &blank); + if (res->error == AGENTX_RES_NO_ERROR && reg->reg_hook_ok) + reg->reg_hook_ok(p, res, reg); + else if (res->error != AGENTX_RES_NO_ERROR && reg->reg_hook_fail) + reg->reg_hook_fail(p, res, reg); + + mb_free(reg); + break; + } + } + + if (EMPTY_LIST(p->registration_queue)) + snmp_up(p); } /* @@ -1279,6 +1326,33 @@ snmp_stop_subagent(struct snmp_proto *p) close_pdu(p, AGENTX_CLOSE_SHUTDOWN); } +/* + * snmp_register_mibs - register all MIB subtrees + * @p: SNMP protocol instance + */ +void +snmp_register_mibs(struct snmp_proto *p) +{ + snmp_bgp4_register(p); + ASSUME(!EMPTY_LIST(p->registration_queue)); +} + +/* + * snmp_start_subagent - send session open request + * @p: SNMP protocol instance + * + * Send agentx-Open-PDU with configured OID and string description. + */ +void +snmp_start_subagent(struct snmp_proto *p) +{ + ASSUME(p->state == SNMP_OPEN); + + /* blank oid means unsupported */ + STATIC_OID(0) blank = { 0 }; + open_pdu(p, (struct oid *) &blank); +} + /* * snmp_rx - handle received PDUs in RX buffer in normal operation * @sk: communication socket @@ -1312,6 +1386,22 @@ snmp_rx(sock *sk, uint size) return 0; } +/* + * space_for_response - check if TX buffer has space for agentx-Response-PDU + * @sk: communication socket owned by SNMP protocol instance + * + * In some cases we send only the AgentX header but if we want to signal an + * error, we need at least space for agentx-Response-PDU. This simplifies the + * PDU space requirements testing. + */ +static inline int +space_for_response(const sock *sk) +{ + return ( + (uint) (sk->tbuf + sk->tbsize - sk->tpos) >= sizeof(struct agentx_response) + ); +} + /* * snmp_tx - handle TX buffer * @sk: communication socket owned by SNMP protocol instance @@ -1333,97 +1423,3 @@ snmp_tx(sock *sk) snmp_rx(sk, sk->tpos - sk->tbuf); } - -/* - * snmp_ping - send an agentx-Ping-PDU - * @p: SNMP protocol instance - */ -void -snmp_ping(struct snmp_proto *p) -{ - if (!snmp_is_active(p)) - return; - - sock *sk = p->sock; - struct snmp_pdu c; - snmp_pdu_context(&c, p, sk); - - if (c.size < AGENTX_HEADER_SIZE) - return; - - int unused = sk->tbuf + sk->tbsize - c.buffer; - if (unused < AGENTX_HEADER_SIZE) - return; - - struct agentx_header *h = (void *) c.buffer; - ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); - snmp_blank_header(h, AGENTX_PING_PDU); - p->packet_id++; - snmp_session(p, h); - if (p->verbose) - TRACE(D_PACKETS, "SNMP sending agentx-Ping-PDU"); - p->ignore_ping_id = p->packet_id; - - /* sending only header */ - uint s = update_packet_size(h, (byte *) h + AGENTX_HEADER_SIZE); - - if (p->packet_id) - p->pings++; - sk_send(sk, s); -} - -/* - * snmp_tbuf_reserve - conditionally grow the TX buffer - * @c: transmit PDU context - * @size: size to make available - * - * Return non-zero if the buffer was relocated. - */ -int -snmp_tbuf_reserve(struct snmp_pdu *c, size_t size) -{ - if (size >= c->size) - { - struct snmp_proto *p = c->p; - sock *sk = p->sock; - - int start_diff; - if (c->sr_vb_start != NULL) - start_diff = (char *) c->sr_vb_start - (char *) sk->tbuf; - - sk_set_tbsize(sk, sk->tbsize + 2048); - c->size += 2048; - - if (c->sr_vb_start != NULL) - c->sr_vb_start = (struct agentx_varbind *) (sk->tbuf + start_diff); - - return 1; - } - - return 0; -} - -/* - * prepare_response - fill buffer with AgentX PDU header - * @p: SNMP protocol instance - * @c: transmit PDU context to use - * - * Prepare known parts of AgentX packet header into the TX buffer held by @c. - */ -static struct agentx_response * -prepare_response(struct snmp_proto *p, struct snmp_pdu *c) -{ - struct agentx_response *r = (void *) c->buffer; - struct agentx_header *h = &r->h; - - snmp_blank_header(h, AGENTX_RESPONSE_PDU); - snmp_session(p, h); - - /* protocol doesn't care about subagent upTime */ - STORE_U32(r->uptime, 0); - STORE_U16(r->error, AGENTX_RES_NO_ERROR); - STORE_U16(r->index, 0); - - ADVANCE(c->buffer, c->size, sizeof(struct agentx_response)); - return r; -} diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index 3e922d92..18bd1df1 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -6,10 +6,6 @@ #include "snmp.h" #include "lib/macro.h" -void snmp_start_subagent(struct snmp_proto *p); -void snmp_stop_subagent(struct snmp_proto *p); -void snmp_ping(struct snmp_proto *p); - #define AGENTX_VERSION 1 /* standard snmp internet prefix */ @@ -196,11 +192,6 @@ struct agentx_varbind { /* AgentX variable binding data optionally here */ }; -struct agentx_search_range { - struct oid *start; - struct oid *end; -}; - /* AgentX Octet String */ struct agentx_octet_str { u32 length; @@ -333,6 +324,7 @@ struct snmp_pdu { u32 index; /* index on which the error was found */ }; +#if 0 struct snmp_packet_info { node n; u8 type; // enum type @@ -341,14 +333,18 @@ struct snmp_packet_info { u32 packet_id; void *data; }; +#endif +void snmp_start_subagent(struct snmp_proto *p); +void snmp_stop_subagent(struct snmp_proto *p); +void snmp_ping(struct snmp_proto *p); int snmp_rx(sock *sk, uint size); void snmp_tx(sock *sk); -int snmp_rx_stop(sock *sk, uint size); void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance); void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len); void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime); - +void snmp_register_mibs(struct snmp_proto *p); +struct agentx_varbind *snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid); int snmp_tbuf_reserve(struct snmp_pdu *c, size_t bytes); static inline int @@ -359,9 +355,5 @@ snmp_is_active(const struct snmp_proto *p) p->state == SNMP_CONN; } -struct agentx_varbind *snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid); -u8 snmp_get_mib_class(const struct oid *oid); - -void snmp_register_mibs(struct snmp_proto *p); #endif