From e8cad1f2750f804d0e26f1e8dc13029cb2fe31b7 Mon Sep 17 00:00:00 2001 From: Vojtech Vilimek Date: Wed, 10 Jan 2024 12:21:46 +0100 Subject: [PATCH] SNMP: dead end The code contains hard to debug bug where we periodically do some kind of error. The problem is caused by weird values after the AgentX PDU header, exactly where the first OID should lie. We expend value less than 15 but we found values like 0x00003b47. We found possible cause on assignment to socket's receive buffer position in proto/snmp/subagent.c:snmp_rx at line 1569. An erroneous behavior may have been caused by off-by-on error. More investigation is needed to gain full picture. To resolve this issue we use much more simpler approach. We will set max packet size and wait until whole packet has arrived. --- proto/snmp/bgp_mib.c | 144 +++++------- proto/snmp/bgp_mib.h | 12 + proto/snmp/config.Y | 14 +- proto/snmp/snmp.c | 115 ++++++--- proto/snmp/snmp.h | 29 ++- proto/snmp/snmp_utils.c | 397 +++++++++++++++++++++++++------ proto/snmp/snmp_utils.h | 23 +- proto/snmp/subagent.c | 510 +++++++++++++++++++--------------------- proto/snmp/subagent.h | 33 ++- 9 files changed, 781 insertions(+), 496 deletions(-) diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index 7eb7d613..c55af290 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -13,6 +13,13 @@ #include "subagent.h" #include "bgp_mib.h" +STATIC_ASSERT(BGP_MIB_IDLE == BS_IDLE + 1); +STATIC_ASSERT(BGP_MIB_CONNECT == BS_CONNECT + 1); +STATIC_ASSERT(BGP_MIB_ACTIVE == BS_ACTIVE + 1); +STATIC_ASSERT(BGP_MIB_OPENSENT == BS_OPENSENT + 1); +STATIC_ASSERT(BGP_MIB_OPENCONFIRM == BS_OPENCONFIRM + 1); +STATIC_ASSERT(BGP_MIB_ESTABLISHED == BS_ESTABLISHED + 1); + /* hash table macros */ #define SNMP_HASH_KEY(n) n->peer_ip #define SNMP_HASH_NEXT(n) n->next @@ -27,43 +34,25 @@ static inline void ip4_to_oid(struct oid *oid, ip4_addr addr); -/* BGP_MIB states see enum BGP_INTERNAL_STATES */ -static const char * const debug_bgp_states[] UNUSED = { - [BGP_INTERNAL_INVALID] = "BGP_INTERNAL_INVALID", - [BGP_INTERNAL_BGP] = "BGP_INTERNAL_BGP", - [BGP_INTERNAL_VERSION] = "BGP_INTERNAL_VERSION", - [BGP_INTERNAL_LOCAL_AS] = "BGP_INTERNAL_LOCAL_AS", - [BGP_INTERNAL_PEER_TABLE] = "BGP_INTERNAL_PEER_TABLE", - [BGP_INTERNAL_PEER_ENTRY] = "BGP_INTERNAL_PEER_ENTRY", - [BGP_INTERNAL_PEER_IDENTIFIER] = "BGP_INTERNAL_PEER_IDENTIFIER", - [BGP_INTERNAL_STATE] = "BGP_INTERNAL_STATE", - [BGP_INTERNAL_ADMIN_STATUS] = "BGP_INTERNAL_ADMIN_STATUS", - [BGP_INTERNAL_NEGOTIATED_VERSION] = "BGP_INTERNAL_NEGOTIATED_VERSION", - [BGP_INTERNAL_LOCAL_ADDR] = "BGP_INTERNAL_LOCAL_ADDR", - [BGP_INTERNAL_LOCAL_PORT] = "BGP_INTERNAL_LOCAL_PORT", - [BGP_INTERNAL_REMOTE_ADDR] = "BGP_INTERNAL_REMOTE_ADDR", - [BGP_INTERNAL_REMOTE_PORT] = "BGP_INTERNAL_REMOTE_PORT", - [BGP_INTERNAL_REMOTE_AS] = "BGP_INTERNAL_REMOTE_AS", - [BGP_INTERNAL_RX_UPDATES] = "BGP_INTERNAL_RX_UPDATES", - [BGP_INTERNAL_TX_UPDATES] = "BGP_INTERNAL_TX_UPDATES", - [BGP_INTERNAL_RX_MESSAGES] = "BGP_INTERNAL_RX_MESSAGES", - [BGP_INTERNAL_TX_MESSAGES] = "BGP_INTERNAL_TX_MESSAGES", - [BGP_INTERNAL_LAST_ERROR] = "BGP_INTERNAL_LAST_ERROR", - [BGP_INTERNAL_FSM_TRANSITIONS] = "BGP_INTERNAL_FSM_TRANSITIONS", - [BGP_INTERNAL_FSM_ESTABLISHED_TIME] = "BGP_INTERNAL_FSM_ESTABLISHED_TIME", - [BGP_INTERNAL_RETRY_INTERVAL] = "BGP_INTERNAL_RETRY_INTERVAL", - [BGP_INTERNAL_HOLD_TIME] = "BGP_INTERNAL_HOLD_TIME", - [BGP_INTERNAL_KEEPALIVE] = "BGP_INTERNAL_KEEPALIVE", - [BGP_INTERNAL_HOLD_TIME_CONFIGURED] = "BGP_INTERNAL_HOLD_TIME_CONFIGURED", - [BGP_INTERNAL_KEEPALIVE_CONFIGURED] = "BGP_INTERNAL_KEEPALIVE_CONFIGURED", - [BGP_INTERNAL_ORIGINATION_INTERVAL] = "BGP_INTERNAL_ORIGINATION_INTERVAL", - [BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT] = "BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT", - [BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME] = "BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME", - [BGP_INTERNAL_PEER_TABLE_END] = "BGP_INTERNAL_PEER_TABLE_END", - [BGP_INTERNAL_IDENTIFIER] = "BGP_INTERNAL_IDENTIFIER", - [BGP_INTERNAL_END] = "BGP_INTERNAL_END", - [BGP_INTERNAL_NO_VALUE] = "BGP_INTERNAL_NO_VALUE", -}; + +static inline void +snmp_hash_add_peer(struct snmp_proto *p, struct snmp_bgp_peer *peer) +{ + HASH_INSERT(p->bgp_hash, SNMP_HASH, peer); +} + +static inline struct snmp_bgp_peer * +snmp_hash_find(struct snmp_proto *p, ip4_addr key) +{ + return HASH_FIND(p->bgp_hash, SNMP_HASH, key); +} + +static inline void +snmp_bgp_last_error(const struct bgp_proto *bgp, char err[2]) +{ + err[0] = bgp->last_error_code & 0x00FF0000 >> 16; + err[1] = bgp->last_error_code & 0x000000FF; +} static u8 bgp_get_candidate(u32 field) @@ -107,25 +96,6 @@ 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 @@ -252,12 +222,13 @@ snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response UNUSED *r, stru static void snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_error[], uint state_val) { - // TODO remove heap allocation, put the data on stack - #define SNMP_OID_SIZE_FROM_LEN(x) (sizeof(struct oid) + (x) * sizeof(u32)) + /* OIDs, VB type headers, octet string, ip4 address, integer */ + uint sz = 3 * SNMP_OID_SIZE_FROM_LEN(9) + 3 * 4 + 8 + 8 + 4; + /* trap OID bgpEstablishedNotification (.1.3.6.1.2.1.0.1) */ - struct oid *head = mb_alloc(p->pool, SNMP_OID_SIZE_FROM_LEN(3)); + struct oid *head = mb_alloc(p->pool, SNMP_OID_SIZE_FROM_LEN(3)) + sz; head->n_subid = 3; head->prefix = 2; head->include = head->pad = 0; @@ -266,15 +237,13 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ for (uint i = 0; i < head->n_subid; i++) head->ids[i] = trap_ids[i]; - /* OIDs, VB type headers, octet string, ip4 address, integer */ - uint sz = 3 * SNMP_OID_SIZE_FROM_LEN(9) + 3 * 4 + 8 + 8 + 4; - /* Paylaod OIDs */ + void *data = (void *) head + sz; - void *data = mb_alloc(p->pool, sz); struct agentx_varbind *addr_vb = data; + // TODO remove magic constants; use measuring functions instead /* +4 for varbind header, +8 for octet string */ - struct agentx_varbind *error_vb = data + SNMP_OID_SIZE_FROM_LEN(9) + 4 + 8; + struct agentx_varbind *error_vb = data + SNMP_OID_SIZE_FROM_LEN(9) + 4 + 8; struct agentx_varbind *state_vb = (void *) error_vb + SNMP_OID_SIZE_FROM_LEN(9) + 4 + 8; addr_vb->pad = error_vb->pad = state_vb->pad = 0; @@ -330,7 +299,7 @@ snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto) const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn; if (bgp_conn) - return bgp_conn->state; + return bgp_conn->state + 1; if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE && MIN(bgp_in->state, bgp_out->state) != BS_CLOSE) @@ -512,7 +481,7 @@ snmp_bgp_has_value(u8 state) * @state: BGP linearized state * * Returns @state if has value in BGP4-MIB, zero otherwise. Used for Get-PDU - * ackets. + * packets. */ u8 snmp_bgp_get_valid(u8 state) @@ -753,7 +722,6 @@ update_bgp_oid(struct oid *oid, u8 state) #undef SNMP_UPDATE_CASE } -// TODO test bgp_find_dynamic_oid static struct oid * bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, const struct oid *o_end, u8 start_state) { @@ -782,20 +750,20 @@ bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, const struct oid trie_walk_init(&ws, p->bgp_trie, NULL, 0); - if (trie_walk_next(&ws, &net)) - { - /* - * If the o_end is empty, then there are no conditions on the ip4 address. - */ - int cmp = ip4_compare(net4_prefix(&net), dest); - if (cmp < 0 || (cmp == 0 && snmp_is_oid_empty(o_end))) - { - // TODO repair - struct oid *o = snmp_oid_duplicate(p->pool, o_start); - snmp_oid_ip4_index(o, 5, net4_prefix(&net)); + if (!trie_walk_next(&ws, &net)) + return NULL; - return o; - } + /* + * If the o_end is empty, then there are no conditions on the ip4 address. + */ + int cmp = ip4_compare(net4_prefix(&net), dest); + if (cmp < 0 || (cmp == 0 && snmp_is_oid_empty(o_end))) + { + // TODO repair + struct oid *o = snmp_oid_duplicate(p->pool, o_start); + snmp_oid_ip4_index(o, 5, net4_prefix(&net)); + + return o; } return NULL; @@ -1005,8 +973,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, return pkt; } - // TODO XXX deal with possible change of (remote) ip; BGP should restart and - // disappear struct snmp_bgp_peer *pe = snmp_hash_find(p, addr); if (!pe) @@ -1018,7 +984,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *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; @@ -1028,7 +993,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *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); @@ -1041,21 +1005,21 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, 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); + uint fsm_state = snmp_bgp_fsm_state(bgp_proto); char last_error[2]; snmp_bgp_last_error(bgp_proto, last_error); switch (state) { case BGP_INTERNAL_PEER_IDENTIFIER: - if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED) + if (fsm_state == BGP_MIB_OPENCONFIRM || fsm_state == BGP_MIB_ESTABLISHED) pkt = snmp_varbind_ip4(vb, size, ip4_from_u32(bgp_proto->remote_id)); else pkt = snmp_varbind_ip4(vb, size, IP4_NONE); break; case BGP_INTERNAL_STATE: - pkt = snmp_varbind_int(vb, size, bgp_state); + pkt = snmp_varbind_int(vb, size, fsm_state); break; case BGP_INTERNAL_ADMIN_STATUS: @@ -1067,7 +1031,7 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, break; case BGP_INTERNAL_NEGOTIATED_VERSION: - if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED) + if (fsm_state == BGP_MIB_ESTABLISHED || fsm_state == BGP_MIB_ESTABLISHED) pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_VALUE); else pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_NO_VALUE); @@ -1075,7 +1039,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, break; case BGP_INTERNAL_LOCAL_ADDR: - // TODO XXX bgp_proto->link_addr & zero local_ip pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->local_ip)); break; @@ -1235,8 +1198,7 @@ UNUSED, uint contid UNUSED, u8 state) break; case BGP_INTERNAL_IDENTIFIER: - // TODO make a check - pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(p->bgp_local_id)); + pkt = snmp_varbind_ip4(vb, size, p->bgp_local_id); break; default: diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index c3eaa987..4fcd96ae 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -33,9 +33,12 @@ enum BGP4_MIB_PEER_TABLE { } PACKED; /* version of BGP, here BGP-4 */ +#define BGP4_VERSIONS ((char[]) { 0x10 }) /* OID bgp.bgpVersion */ +/* for OID bgp.bgpPeerTable.bgpPeerEntry.bgpPeerNegotiatedVersion */ #define SNMP_BGP_NEGOTIATED_VER_VALUE 4 #define SNMP_BGP_NEGOTIATED_VER_NO_VALUE 0 + void snmp_bgp_register(struct snmp_proto *p); void 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 *r, struct oid *oid); @@ -96,4 +99,13 @@ enum BGP_INTERNAL_STATES { BGP_INTERNAL_NO_VALUE = 255, } PACKED; +enum bgp4_mib_bgp_states { + BGP_MIB_IDLE = 1, + BGP_MIB_CONNECT = 2, + BGP_MIB_ACTIVE = 3, + BGP_MIB_OPENSENT = 4, + BGP_MIB_OPENCONFIRM = 5, + BGP_MIB_ESTABLISHED = 6, +}; + #endif diff --git a/proto/snmp/config.Y b/proto/snmp/config.Y index 05e7cf38..9067cf80 100644 --- a/proto/snmp/config.Y +++ b/proto/snmp/config.Y @@ -40,10 +40,10 @@ snmp_proto_item: if (($3 < 1) || ($3 > 65535)) cf_error("Invalid port number"); SNMP_CFG->remote_port = $3; } - | 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"); + | LOCAL ID IP4 { SNMP_CFG->bgp_local_id = $3; } + | LOCAL ADDRESS IP4 { SNMP_CFG->local_ip = $3; } + | REMOTE ADDRESS IP4 { + if (ip4_zero($3)) cf_error("Invalid remote ip address"); SNMP_CFG->remote_ip = $3; } | LOCAL AS expr { @@ -86,9 +86,9 @@ snmp_proto_start: proto_start SNMP init_list(&SNMP_CFG->bgp_entries); SNMP_CFG->bonds = 0; - SNMP_CFG->local_ip = IPA_NONE; - SNMP_CFG->remote_ip = ipa_build4(127,0,0,1); - SNMP_CFG->bgp_local_id = IPA_NONE; + SNMP_CFG->local_ip = IP4_NONE; + SNMP_CFG->remote_ip = ip4_build(127,0,0,1); + SNMP_CFG->bgp_local_id = IP4_NONE; SNMP_CFG->local_port = 0; SNMP_CFG->remote_port = 705; SNMP_CFG->bgp_local_as = 0; diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index 85fcd797..fbff7270 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -1,6 +1,5 @@ /* - * BIRD -- Simple Network Management Protocol (SNMP) - * + * BIRD -- Simple Network Management Protocol (SNMP) * * (c) 2022 Vojtech Vilimek * (c) 2022 CZ.NIC z.s.p.o. * @@ -135,11 +134,12 @@ static void snmp_cleanup(struct snmp_proto *p); static int snmp_rx_skip(sock UNUSED *sk, uint UNUSED size) { + log(L_INFO "snmp_rx_skip with size %u", size); return 1; } /* - * snmp_tx - handle empty TX-buffer during session reset + * snmp_tx_skip - 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 @@ -147,10 +147,11 @@ snmp_rx_skip(sock UNUSED *sk, uint UNUSED size) * resetting the established session. If called we direcly reset the session. */ static void -snmp_tx(sock *sk) +snmp_tx_skip(sock *sk) { + log(L_INFO "snmp_tx_skip()"); struct snmp_proto *p = sk->data; - snmp_sock_disconnect(p, 1); + snmp_set_state(p, SNMP_DOWN); } /* @@ -168,11 +169,15 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state) p->state = state; if (last == SNMP_RESET) + { rfree(p->sock); + p->sock = NULL; + } switch (state) { case SNMP_INIT: + debug("snmp -> SNMP_INIT\n"); ASSERT(last == SNMP_DOWN); struct object_lock *lock; lock = p->lock = olock_new(p->pool); @@ -190,11 +195,13 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state) break; case SNMP_LOCKED: + debug("snmp -> SNMP_LOCKED\n"); ASSERT(last == SNMP_INIT || SNMP_RESET); + snmp_unset_header(p); sock *s = sk_new(p->pool); s->type = SK_TCP_ACTIVE; - s->saddr = p->local_ip; - s->daddr = p->remote_ip; + s->saddr = ipa_from_ip4(p->local_ip); + s->daddr = ipa_from_ip4(p->remote_ip); s->dport = p->remote_port; s->rbsize = SNMP_RX_BUFFER_SIZE; s->tbsize = SNMP_TX_BUFFER_SIZE; @@ -216,32 +223,40 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state) break; case SNMP_OPEN: + debug("snmp -> SNMP_OPEN\n"); ASSERT(last == SNMP_LOCKED); p->sock->rx_hook = snmp_rx; p->sock->tx_hook = NULL; + log(L_WARN " Staring subagent %u %u %u %u", p->header_offset, p->last_index, p->last_size, p->last_pkt_id); snmp_start_subagent(p); // handle no response (for long time) break; case SNMP_REGISTER: + debug("snmp -> SNMP_REGISTER\n"); ASSERT(last == SNMP_OPEN); snmp_register_mibs(p); break; case SNMP_CONN: + debug("snmp -> SNMP_CONN\n"); ASSERT(last == SNMP_REGISTER); proto_notify_state(&p->p, PS_UP); break; case SNMP_STOP: + debug("snmp -> SNMP_STOP\n"); ASSUME(last == SNMP_REGISTER || last == SNMP_CONN); snmp_stop_subagent(p); + p->sock->rx_hook = snmp_rx_skip; + p->sock->tx_hook = snmp_tx_skip; p->startup_timer->hook = snmp_stop_timeout; tm_start(p->startup_timer, p->timeout); proto_notify_state(&p->p, PS_STOP); break; case SNMP_DOWN: + debug("snmp -> SNMP_DOWN\n"); //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 @@ -250,10 +265,11 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state) break; case SNMP_RESET: + debug("snmp -> SNMP_RESET\n"); ASSUME(last == SNMP_REGISTER || last == SNMP_CONN); - ASSERT(p->sock); + ASSUME(p->sock); p->sock->rx_hook = snmp_rx_skip; - p->sock->tx_hook = snmp_tx; + p->sock->tx_hook = snmp_tx_skip; break; default: @@ -276,8 +292,7 @@ snmp_init(struct proto_config *CF) p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS; - /* default starting state */ - //p->state = SNMP_DOWN; // (0) + p->state = SNMP_DOWN; return P; } @@ -380,10 +395,6 @@ snmp_sock_disconnect(struct snmp_proto *p, int reconnect) return; } - //proto_notify_state(&p->p, PS_START); - rfree(p->sock); - p->sock = NULL; - // TODO - wouldn't be better to use inter jump state RESET (soft reset) ? snmp_set_state(p, SNMP_DOWN); @@ -419,7 +430,6 @@ static void snmp_start_locked(struct object_lock *lock) { struct snmp_proto *p = lock->data; -log(L_INFO "SNMP startup timer or btime %t ? %ld", p->startup_delay, p->startup_timer); if (p->startup_delay) { ASSERT(p->startup_timer); @@ -445,6 +455,9 @@ snmp_reconnect(timer *tm) // TODO snmp_set_state(p, SNMP_DOWN); return; + /* reset the connection */ + snmp_sock_disconnect(p, 1); + // TODO better snmp_set_state(SNMP_DOWM); ?? if (p->state == SNMP_STOP || p->state == SNMP_DOWN) return; @@ -589,13 +602,45 @@ snmp_start(struct proto *P) snmp_bgp_start(p); - p->last_header = NULL; + snmp_unset_header(p); snmp_set_state(p, SNMP_INIT); return PS_START; } +static inline int +snmp_reconfigure_logic(struct snmp_proto *p, const struct snmp_config *new) +{ + const struct snmp_config *old = SKIP_BACK(struct snmp_config, cf, p->p.cf); + + if (old->bonds != new->bonds) + return 0; + + uint bonds = old->bonds; + struct snmp_bond *b1, *b2; + WALK_LIST(b1, new->bgp_entries) + { + WALK_LIST(b2, old->bgp_entries) + { + if (!strcmp(b1->config->name, b2->config->name)) + goto skip; + } + + return 0; +skip: + bonds--; + } + + if (bonds != 0) + return 0; + + return !memcmp(((byte *) old) + sizeof(struct proto_config), + ((byte *) new) + sizeof(struct proto_config), + OFFSETOF(struct snmp_config, description) - sizeof(struct proto_config)) + && ! strncmp(old->description, new->description, UINT32_MAX); +} + /* * snmp_reconfigure - Test if SNMP instance is reconfigurable * @P - SNMP protocol generic handle, current state @@ -611,30 +656,22 @@ 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); - struct snmp_bond *b1, *b2; - WALK_LIST(b1, new->bgp_entries) - { - WALK_LIST(b2, old->bgp_entries) - { - if (!strcmp(b1->config->name, b2->config->name)) - goto skip; - } + int retval = snmp_reconfigure_logic(p, new); - return 0; -skip:; + if (retval) { + /* Reinitialize the hash after snmp_shutdown() */ + HASH_INIT(p->bgp_hash, p->pool, 10); + snmp_bgp_start(p); + snmp_unset_header(p); } - return !memcmp(((byte *) old) + sizeof(struct proto_config), - ((byte *) new) + sizeof(struct proto_config), - OFFSETOF(struct snmp_config, description) - sizeof(struct proto_config)) - && ! strncmp(old->description, new->description, UINT32_MAX); + return retval; } /* - * snmp_show_proto_info - Print basic information about SNMP protocol instance - * @P - SNMP protocol generic handle + * 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) @@ -646,22 +683,26 @@ snmp_show_proto_info(struct proto *P) // TODO move me into the bgp_mib.c cli_msg(-1006, " BGP4-MIB"); - cli_msg(-1006, " enabled true"); // TODO + // TODO enabled cli_msg(-1006, " Local AS %u", p->bgp_local_as); cli_msg(-1006, " Local router id %R", p->bgp_local_id); cli_msg(-1006, " BGP peers"); + if (p->state == SNMP_DOWN || p->state == SNMP_RESET) + return; + HASH_WALK(p->bgp_hash, next, peer) { cli_msg(-1006, " protocol name: %s", peer->bgp_proto->p.name); - cli_msg(-1006, " remote IPv4 address: %I4", peer->peer_ip); + cli_msg(-1006, " Remote IPv4 address: %I4", peer->peer_ip); + cli_msg(-1006, " Remote router id %R", peer->bgp_proto->remote_id); } HASH_WALK_END; } /* * snmp_postconfig - Check configuration correctness - * @CF - SNMP procotol configuration generic handle + * @CF: SNMP procotol configuration generic handle */ static void snmp_postconfig(struct proto_config *CF) diff --git a/proto/snmp/snmp.h b/proto/snmp/snmp.h index eecd01c3..35f7817d 100644 --- a/proto/snmp/snmp.h +++ b/proto/snmp/snmp.h @@ -12,6 +12,7 @@ #include "lib/ip.h" #include "lib/socket.h" +#include "lib/resource.h" #include "lib/timer.h" #include "nest/bird.h" #include "nest/protocol.h" @@ -48,12 +49,12 @@ struct snmp_bond { struct snmp_config { struct proto_config cf; - ip_addr local_ip; - ip_addr remote_ip; + ip4_addr local_ip; + ip4_addr remote_ip; u16 local_port; u16 remote_port; - ip_addr bgp_local_id; /* BGP4-MIB related fields */ + ip4_addr bgp_local_id; /* BGP4-MIB related fields */ u32 bgp_local_as; btime timeout; @@ -94,17 +95,28 @@ struct snmp_proto { struct object_lock *lock; pool *pool; /* a shortcut to the procotol mem. pool */ linpool *lp; /* linpool for bgp_trie nodes */ + slab *request_storage; /* manages storages storage for incomming requests */ - ip_addr local_ip; - ip_addr remote_ip; + enum snmp_proto_state state; + + ip4_addr local_ip; + ip4_addr remote_ip; u16 local_port; u16 remote_port; - ip_addr bgp_local_id; /* BGP4-MIB related fields */ + ip4_addr bgp_local_id; /* BGP4-MIB related fields */ u32 bgp_local_as; sock *sock; - void *last_header; /* points to partial PDU header */ + + /* Partial packet processing */ + uint header_offset; /* offset of PDU header in TX-buffer during + * partial parsing */ + uint last_index; /* stores last index during partial parsing */ + uint last_size; /* number of bytes used inside TX-buffer */ + uint last_pkt_id; /* stores packetId to use for partial packet */ + + btime timeout; /* timeout is part of MIB registration. It specifies how long should the master agent wait for request responses. */ @@ -123,10 +135,11 @@ struct snmp_proto { HASH(struct snmp_bgp_peer) bgp_hash; struct tbf rl_gen; + list pending_pdus; + timer *ping_timer; btime startup_delay; timer *startup_timer; - u8 state; }; //void snmp_tx(sock *sk); diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 64a9ce64..cc78f85e 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -11,20 +11,29 @@ #include "snmp_utils.h" inline void -snmp_pdu_context(struct snmp_pdu *pdu, sock *sk) +snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk) { - pdu->buffer = sk->tpos; - pdu->size = sk->tbuf + sk->tbsize - sk->tpos; pdu->error = AGENTX_RES_NO_ERROR; - pdu->index = 0; + if (!snmp_is_partial(p)) + { + pdu->buffer = sk->tpos; + pdu->size = sk->tbuf + sk->tbsize - sk->tpos; + pdu->index = 0; + return; + } + + pdu->buffer = sk->tbuf + p->header_offset + p->last_size; + pdu->size = sk->tbuf + sk->tbsize - pdu->buffer; + pdu->index = p->last_index; } inline void snmp_session(const struct snmp_proto *p, struct agentx_header *h) { - h->session_id = p->session_id; - h->transaction_id = p->transaction_id; - h->packet_id = p->packet_id; + STORE_U32(h->session_id, p->session_id); + STORE_U32(h->transaction_id, p->transaction_id); + STORE_U32(h->packet_id, p->packet_id); + //log(L_INFO "storing packet id %u into the header %p", p->packet_id, h); } inline int @@ -36,7 +45,8 @@ snmp_has_context(const struct agentx_header *h) inline byte * snmp_add_context(struct snmp_proto *p, struct agentx_header *h, uint contid) { - h->flags = h->flags | AGENTX_NON_DEFAULT_CONTEXT; + u8 flags = LOAD_U8(h->flags); + STORE_U8(h->flags, flags | AGENTX_NON_DEFAULT_CONTEXT); // TODO append the context after the header (void)p; (void)contid; @@ -60,7 +70,8 @@ int snmp_is_oid_empty(const struct oid *oid) { if (oid != NULL) - return oid->n_subid == 0 && oid->prefix == 0 && oid->include == 0; + return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 && + LOAD_U8(oid->include) == 0; else return 0; } @@ -76,9 +87,10 @@ snmp_pkt_len(const byte *start, const byte *end) return (end - start) - AGENTX_HEADER_SIZE; } -/** - * - * used for copying oid to in buffer oid @dest +/* + * snmp_oid_copy - copy OID from one place to another + * @dest: destination to use + * @src: OID to be copied from */ void snmp_oid_copy(struct oid *dest, const struct oid *src) @@ -88,12 +100,14 @@ snmp_oid_copy(struct oid *dest, const struct oid *src) STORE_U8(dest->include, src->include ? 1 : 0); STORE_U8(dest->pad, 0); - for (int i = 0; i < src->n_subid; i++) + for (int i = 0; i < LOAD_U8(src->n_subid); i++) STORE_U32(dest->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) @@ -113,6 +127,10 @@ 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() + */ size_t snmp_str_size_from_len(uint len) { @@ -152,79 +170,218 @@ snmp_oid_sizeof(uint n_subid) return sizeof(struct oid) + n_subid * sizeof(u32); } -uint snmp_varbind_hdr_size_from_oid(struct oid *oid) +/* + * snmp_varbind_hdr_size_from_oid - return in-buffer size of VarBind + * @oid: OID used as VarBind's name + * + * This function assume @oid to be not NULL. + */ +uint +snmp_varbind_hdr_size_from_oid(const struct oid *oid) { - return snmp_oid_size(oid) + 4; + ASSUME(oid); + return snmp_oid_size(oid) + OFFSETOF(struct agentx_varbind, name); +} + +/* + * snmp_set_varbind_type - set VarBind's type field + * @vb: Varbind inside TX-buffer + * @t: a valid type to be set + * + * This function assumes valid @t. + */ +inline void +snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t) +{ + ASSUME(t != AGENTX_INVALID); + STORE_U16(vb->type, t); +} + +/* Internal wrapper */ +static inline u16 +snmp_load_varbind_type(const struct agentx_varbind *vb) +{ + return LOAD_U16(vb->type); +} + +/* + * snmp_get_varbind_type - loads a VarBind type + * @vb: VarBind pointer to TX-buffer + * + * This function assumes VarBind with valid type, always call snmp_test_varbind + * for in TX-buffer VarBinds! + */ +inline enum agentx_type +snmp_get_varbind_type(const struct agentx_varbind *vb) +{ + ASSUME(snmp_test_varbind(vb)); + return (enum agentx_type) snmp_load_varbind_type(vb); +} + +static inline uint +snmp_get_octet_size(const struct agentx_octet_str *str) +{ + return LOAD_U32(str->length); } /** - * snmp_vb_size - measure size of varbind in bytes - * @vb: variable binding to use + * snmp_varbind_header_size - measure size of VarBind without data in bytes + * @vb: VarBind to use + * + * Return size including whole OID as well as the VarBind header. */ uint -snmp_varbind_header_size(struct agentx_varbind *vb) +snmp_varbind_header_size(const struct agentx_varbind *vb) { return snmp_varbind_hdr_size_from_oid(&vb->name); } uint -snmp_varbind_size(struct agentx_varbind *vb) +snmp_varbind_size_unsafe(const struct agentx_varbind *vb) { - uint hdr_size = snmp_varbind_header_size(vb); - int s = agentx_type_size(vb->type); + ASSUME(snmp_test_varbind(vb)); - if (s >= 0) - return hdr_size + (uint) s; + enum agentx_type type = snmp_get_varbind_type(vb); + int value_size = agentx_type_size(type); - void *data = snmp_varbind_data(vb); + uint vb_header = snmp_varbind_header_size(vb); - if (vb->type == AGENTX_OBJECT_ID) - return hdr_size + snmp_oid_size((struct oid *) data); + if (value_size == 0) + return vb_header; - /* - * Load length of octet string - * (AGENTX_OCTET_STRING, AGENTX_IP_ADDRESS, AGENTX_OPAQUE) - */ - return hdr_size + snmp_str_size_from_len(LOAD_PTR(data)); + if (value_size > 0) + return vb_header + value_size; + + switch (type) + { + case AGENTX_OBJECT_ID:; + struct oid *oid = snmp_varbind_data(vb); + return vb_header + snmp_oid_size(oid); + + case AGENTX_OCTET_STRING: + case AGENTX_IP_ADDRESS: + case AGENTX_OPAQUE:; + struct agentx_octet_str *string = snmp_varbind_data(vb); + return vb_header + snmp_get_octet_size(string); + + default: + /* Shouldn't happen */ + return 0; + } } -/* test if the varbind has valid type */ +/** + * snmp_varbind_size - get size of in-buffer VarBind + * @vb: VarBind 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 +snmp_varbind_size(const struct agentx_varbind *vb, uint limit) +{ + ASSUME(snmp_test_varbind(vb)); + + if (limit < sizeof(struct agentx_varbind)) + return 0; + + enum agentx_type type = agentx_type_size(snmp_get_varbind_type(vb)); + int s = agentx_type_size(type); + uint vb_header = snmp_varbind_header_size(vb); + + 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; + + switch (type) + { + case AGENTX_OBJECT_ID:; + struct oid *oid = snmp_varbind_data(vb); + 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); + return vb_header + snmp_get_octet_size(os); + + default: + /* This should not happen */ + return 0; + } +} + +/* + * snmp_test_varbind - test validity of VarBind's type + * @vb: VarBind to test + */ int snmp_test_varbind(const struct agentx_varbind *vb) { - if (vb->type == AGENTX_INTEGER || - vb->type == AGENTX_OCTET_STRING || - vb->type == AGENTX_NULL || - vb->type == AGENTX_OBJECT_ID || - vb->type == AGENTX_IP_ADDRESS || - vb->type == AGENTX_COUNTER_32 || - vb->type == AGENTX_GAUGE_32 || - vb->type == AGENTX_TIME_TICKS || - vb->type == AGENTX_OPAQUE || - vb->type == AGENTX_COUNTER_64 || - vb->type == AGENTX_NO_SUCH_OBJECT || - vb->type == AGENTX_NO_SUCH_INSTANCE || - vb->type == AGENTX_END_OF_MIB_VIEW) + ASSUME(vb); + + u16 type = snmp_load_varbind_type(vb); + if (type == AGENTX_INTEGER || + type == AGENTX_OCTET_STRING || + type == AGENTX_NULL || + type == AGENTX_OBJECT_ID || + type == AGENTX_IP_ADDRESS || + type == AGENTX_COUNTER_32 || + type == AGENTX_GAUGE_32 || + type == AGENTX_TIME_TICKS || + type == AGENTX_OPAQUE || + type == AGENTX_COUNTER_64 || + type == AGENTX_NO_SUCH_OBJECT || + type == AGENTX_NO_SUCH_INSTANCE || + type == AGENTX_END_OF_MIB_VIEW) return 1; else return 0; } +/* + * snmp_create_varbind - create a null-typed VarBind in buffer + * @buf: buffer to use + */ +struct agentx_varbind * +snmp_create_varbind_null(byte *buf) +{ + struct oid o = { 0 }; + struct agentx_varbind *vb = snmp_create_varbind(buf, &o); + snmp_set_varbind_type(vb, AGENTX_NULL); + return vb; +} + +/* + * snmp_create_varbind - initialize in-buffer non-typed VarBind + * @buf: pointer to first unused buffer byte + * @oid: OID to use as VarBind name + */ struct agentx_varbind * snmp_create_varbind(byte *buf, struct oid *oid) { - struct agentx_varbind *vb = (void*) buf; - vb->pad = 0; + struct agentx_varbind *vb = (void *) buf; + STORE_U16(vb->pad, 0); snmp_oid_copy(&vb->name, oid); return vb; } +#if 0 byte * snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new) { memcpy(&vb->name, new, snmp_oid_size(new)); return (void *) vb + snmp_varbind_header_size(vb); } +#endif /** * snmp_oid_ip4_index - check IPv4 address validity in oid @@ -258,20 +415,27 @@ snmp_valid_ip4_index_unsafe(const struct oid *o, uint start) return 1; } +/* + * snmp_put_nstr - copy c-string into buffer with limit + * @buf: destination buffer + * @str: string to use + * @len: number of characters to use from string + */ byte * snmp_put_nstr(byte *buf, const char *str, uint len) { uint alen = BIRD_ALIGN(len, 4); - STORE_PTR(buf, len); - buf += 4; - memcpy(buf, str, len); + struct agentx_octet_str *octet = (void *) buf; + STORE_U32(octet->length, len); + memcpy(&octet->data, str, len); + buf += len + sizeof(octet->length); /* Insert zero padding in the gap at the end */ for (uint i = 0; i < alen - len; i++) - buf[len + i] = '\0'; + STORE_U8(buf[i], '\0'); - return buf + alen; + return buf + (alen - len); } /** @@ -296,6 +460,7 @@ snmp_put_ip4(byte *buf, ip4_addr addr) /* octet string has size 4 bytes */ STORE_PTR(buf, 4); + /* Always use Network byte order */ put_u32(buf+4, ip4_to_u32(addr)); return buf + 8; @@ -328,15 +493,11 @@ snmp_put_oid(byte *buf, struct oid *oid) * * Put @data into buffer @buf with 3B zeroed padding. */ -/* paste data at first byte in message - * with 3B of padding - */ byte * snmp_put_fbyte(byte *buf, u8 data) { - //log(L_INFO "paste_fbyte()"); - put_u8(buf, data); - put_u24(++buf, 0); // PADDING + STORE_U8(*buf++, data); + memset(buf, 0, 3); /* we fill the 24bit padding with zeros */ return buf + 3; } @@ -394,9 +555,6 @@ snmp_oid_dump(const struct oid *oid) int snmp_oid_compare(const struct oid *left, const struct oid *right) { - const u32 INTERNET_PREFIX[] = {1, 3, 6, 1}; - - if (left->prefix == 0 && right->prefix == 0) goto test_ids; @@ -406,9 +564,9 @@ snmp_oid_compare(const struct oid *left, const struct oid *right) if (left->prefix == 0) { for (int i = 0; i < 4; i++) - if (left->ids[i] < INTERNET_PREFIX[i]) + if (left->ids[i] < snmp_internet[i]) return -1; - else if (left->ids[i] > INTERNET_PREFIX[i]) + else if (left->ids[i] > snmp_internet[i]) return 1; for (int i = 0; i < MIN(left->n_subid - 4, right->n_subid); i++) @@ -453,7 +611,9 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class) r->session_id = p->session_id; /* will be incremented by snmp_session() macro during packet assembly */ r->transaction_id = p->transaction_id; + // TODO where is incremented? is this valid? r->packet_id = p->packet_id + 1; + log(L_INFO "using registration packet_id %u", r->packet_id); r->mib_class = mib_class; @@ -465,6 +625,7 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class) int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class) { + log(L_INFO "snmp_reg_same() r->packet_id %u p->packet_id %u", r->packet_id, h->packet_id); return (r->mib_class == class) && (r->session_id == h->session_id) && @@ -483,8 +644,11 @@ snmp_dump_packet(byte UNUSED *pkt, uint size) } /* - * Returns length of agentx_type @type in bytes. - * Variable length types result in -1. + * agentx_type_size - get in packet VarBind type size + * @type: VarBind type + * + * Returns length of agentx_type @type in bytes, Variable length types result in + * -1. */ int agentx_type_size(enum agentx_type type) @@ -518,10 +682,11 @@ snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type, if (size < (uint) agentx_type_size(type)) return NULL; - vb->type = type; + snmp_set_varbind_type(vb, type); u32 *data = snmp_varbind_data(vb); - *data = val; - return (byte *)(data + 1); + STORE_PTR(data, val); /* note that the data has u32 type */ + data++; + return (byte *) data; } inline byte * @@ -556,17 +721,18 @@ snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr) if (size < snmp_str_size_from_len(4)) return NULL; - vb->type = AGENTX_IP_ADDRESS; + snmp_set_varbind_type(vb, AGENTX_IP_ADDRESS); return snmp_put_ip4(snmp_varbind_data(vb), addr); } +// TODO doc string, we have already the varbind prepared inline byte * snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len) { if (size < snmp_str_size_from_len(len)) return NULL; - vb->type = AGENTX_OCTET_STRING; + snmp_set_varbind_type(vb, AGENTX_OCTET_STRING); return snmp_put_nstr(snmp_varbind_data(vb), str, len); } @@ -574,7 +740,7 @@ inline enum agentx_type snmp_search_res_to_type(enum snmp_search_res r) { ASSUME(r != SNMP_SEARCH_OK); - static enum agentx_type type_arr[] = { + enum agentx_type type_arr[] = { [SNMP_SEARCH_NO_OBJECT] = AGENTX_NO_SUCH_OBJECT, [SNMP_SEARCH_NO_INSTANCE] = AGENTX_NO_SUCH_INSTANCE, [SNMP_SEARCH_END_OF_VIEW] = AGENTX_END_OF_MIB_VIEW, @@ -583,3 +749,88 @@ snmp_search_res_to_type(enum snmp_search_res r) return type_arr[r]; } +inline int +snmp_test_close_reason(byte value) +{ + if (value >= (byte) AGENTX_CLOSE_OTHER && + value <= (byte) AGENTX_CLOSE_BY_MANAGER) + return 1; + else + return 0; +} + +inline struct agentx_header * +snmp_create_tx_header(struct snmp_proto *p, byte *tbuf) +{ + /* the response is created always in TX-buffer */ + p->header_offset = tbuf - p->sock->tbuf; + ASSERT(p->header_offset < p->sock->tbsize); + return (struct agentx_header *) tbuf; +} + +/* + * Partial header manipulation functions + */ + +/* + * snmp_is_partial - check if we have a partially parted packet in TX-buffer + * @p: SNMP protocol instance + */ +inline int +snmp_is_partial(const struct snmp_proto *p) +{ + return p->last_size > 0; +} + +/* + * snmp_get_header - restore partial packet's header from TX-buffer + * @p: SNMP protocol instance + */ +inline struct agentx_header * +snmp_get_header(const struct snmp_proto *p) +{ + /* Nonzero last size indicates existence of partial packet */ + ASSERT(p->last_size && p->header_offset < p->sock->tbsize); + return (struct agentx_header *) (p->sock->tbuf + p->header_offset); +} + +/* + * snmp_set_header - store partial packet's header into protocol + * @p: SNMP protocol instance + * @h: header of the currently parsed PDU + * @c: SNMP PDU context + * + * Store the needed values regarding later partial PDU processing. + */ +inline void +snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c) +{ + sock *sk = p->sock; + // TODO agentx_headier in last_size or not? + ASSERT(c->buffer - sk->tpos >= AGENTX_HEADER_SIZE); + p->last_size = c->buffer - sk->tpos + p->last_size; + p->header_offset = (((byte *) h) - sk->tbuf); + p->last_index = c->index; + log(L_INFO "using p->packet_id %u as a p->last_pkt_id %u", p->packet_id, p->last_pkt_id); + p->last_pkt_id = p->packet_id; + log(L_INFO "snmp_set_header() tbuf %p tpos %p buffer %p header %p header2 %p offset %u last_size %u last_index %u last_pkt_id %u", + sk->tbuf, sk->tpos, c->buffer,h,(byte*)h, p->header_offset, p->last_size, + p->last_index, p->last_pkt_id); +} + +/* + * snmp_unset_header - clean partial packet's header + * @p: SNMP protocol instance + * + * Clean the partial packet processing fields of protocol when the packet is + * fully processed. + */ +inline void +snmp_unset_header(struct snmp_proto *p) +{ + p->last_size = 0; + p->header_offset = 0; + p->last_index = 0; + p->last_pkt_id = 0; +} + diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index fe371ee0..52ba1478 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -11,13 +11,14 @@ int snmp_valid_ip4_index(const struct oid *o, uint start); int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start); uint snmp_oid_size(const struct oid *o); size_t snmp_oid_sizeof(uint n_subid); -uint snmp_varbind_hdr_size_from_oid(struct oid *oid); -uint snmp_varbind_header_size(struct agentx_varbind *vb); -uint snmp_varbind_size(struct agentx_varbind *vb); +uint snmp_varbind_hdr_size_from_oid(const struct oid *oid); +uint snmp_varbind_header_size(const struct agentx_varbind *vb); +uint snmp_varbind_size(const struct agentx_varbind *vb, uint limit); +uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb); int snmp_test_varbind(const struct agentx_varbind *vb); 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, sock *sk); +void snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk); void snmp_oid_copy(struct oid *dest, const struct oid *src); @@ -25,7 +26,8 @@ struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid); struct oid *snmp_oid_blank(struct snmp_proto *p); void *snmp_varbind_data(const struct agentx_varbind *vb); -struct agentx_varbind *snmp_create_varbind(byte* buf, struct oid *oid); +struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid); +struct agentx_varbind *snmp_create_varbind_null(byte *buf); byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new); int snmp_oid_compare(const struct oid *first, const struct oid *second); @@ -46,11 +48,15 @@ void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr); void snmp_oid_dump(const struct oid *oid); +void snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t); +enum agentx_type snmp_get_varbind_type(const struct agentx_varbind *vb); + //struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o); struct snmp_registration *snmp_registration_create(struct snmp_proto *p, u8 mib_class); int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class); +/* Functions filling buffer a typed value */ byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val); byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val); byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val); @@ -64,5 +70,12 @@ enum agentx_type snmp_search_res_to_type(enum snmp_search_res res); int agentx_type_size(enum agentx_type t); +int snmp_test_close_reason(byte value); + +struct agentx_header *snmp_create_tx_header(struct snmp_proto *p, byte *rbuf); +int snmp_is_partial(const struct snmp_proto *p); +struct agentx_header *snmp_get_header(const struct snmp_proto *p); +void snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c); +void snmp_unset_header(struct snmp_proto *p); #endif diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 804cfa1e..5da01baf 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -39,7 +39,7 @@ * communication socket. This implicitly closes the established session. We * chose this approach because we cannot easily mark the boundary between packets. * When we are reseting the connection, we change the snmp_state to SNMP_RESET. - * In SNMP_RESET state we skip all received bytes and wait for snmp_tx() + * In SNMP_RESET state we skip all received bytes and wait for snmp_tx_skip() * to be called. The socket's tx_hook is called when the TX-buffer is empty, * meaning our response (agentx-Response-PDU) was send. * @@ -66,12 +66,20 @@ static uint parse_response(struct snmp_proto *p, byte *buf, uint size); static void do_response(struct snmp_proto *p, byte *buf, uint size); static uint parse_gets2_pdu(struct snmp_proto *p, byte *buf, uint size, uint *skip); static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c); -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 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 snmp_proto *p, struct agentx_header *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); u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET }; +static inline int +snmp_is_active(struct snmp_proto *p) +{ + /* Note: states in which we have opened socket */ + return p->state == SNMP_OPEN || p->state == SNMP_REGISTER || + p->state == SNMP_CONN; +} + static inline void snmp_header(struct agentx_header *h, enum agentx_pdu_types type, u8 flags) { @@ -179,20 +187,21 @@ snmp_error(struct snmp_proto *p) * @p: SNMP protocol instance * @error: PDU error fields value * @index: PDU error index field value + * + * This function assumes that the buffer has enough space to fill in the AgentX + * Response PDU. So it is the responsibility of the caller to provide that. */ static void snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16 index) { sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); - // XXX: THIS SHOULDN'T HAPPEN! - fix it - if (c.size < sizeof(struct agentx_response)) - snmp_manage_tbuf(p, &c); + ASSUME(c.size >= sizeof(struct agentx_response)); struct agentx_response *res = prepare_response(p, &c); - response_err_ind(res, error, index); + response_err_ind(p, res, error, index); sk_send(sk, sizeof(struct agentx_response)); } @@ -201,7 +210,7 @@ snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16 * @p: SNMP protocol instance * @oid: PDU OID description field value * - * Other fields are filled based on @p configuratin (timeout, subagent string + * Other fields are filled based on @p configuration (timeout, subagent string * description) */ static void @@ -211,14 +220,14 @@ open_pdu(struct snmp_proto *p, struct oid *oid) sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); #define TIMEOUT_SIZE 4 /* 1B timeout, 3B zero padding */ if (c.size < AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) + + snmp_str_size(cf->description)) snmp_manage_tbuf(p, &c); - struct agentx_header *h = (struct agentx_header *) c.buffer; + struct agentx_header *h = snmp_create_tx_header(p, c.buffer); ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_blank_header(h, AGENTX_OPEN_PDU); @@ -228,7 +237,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid) c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description)); - if (p->timeout >= 1 TO_US && p->timeout <= 255 TO_US) + 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); @@ -241,7 +250,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid) c.buffer = snmp_put_oid(c.buffer, oid); c.buffer = snmp_put_str(c.buffer, cf->description); - uint s = update_packet_size(p, sk->tpos, c.buffer); + uint s = update_packet_size(p, h, c.buffer); sk_send(sk, s); #undef TIMEOUT_SIZE } @@ -260,10 +269,10 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); #define UPTIME_SIZE \ - (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } )*/ + (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } ) */ #define TRAP0_HEADER_SIZE \ (7 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[6] } ) */ @@ -276,37 +285,44 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in if (c.size < sz) snmp_manage_tbuf(p, &c); - struct agentx_header *h = (struct agentx_header *) c.buffer; + struct agentx_header *h = snmp_create_tx_header(p, c.buffer); ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_blank_header(h, AGENTX_NOTIFY_PDU); p->packet_id++; + log(L_INFO "incrementing packet_id to %u (notify)", p->packet_id); snmp_session(p, h); if (include_uptime) { /* sysUpTime.0 oid */ - struct oid uptime = { + struct oid uptime_oid = { .n_subid = 4, .prefix = SNMP_MGMT, .include = 0, .pad = 0, }; + /* {mgmt}.mib-2.system.sysUpTime.sysUpTimeInstance (0) */ u32 uptime_ids[] = { 1, 1, 3, 0 }; - struct agentx_varbind *vb = snmp_create_varbind(c.buffer, &uptime); - for (uint i = 0; i < uptime.n_subid; i++) + struct agentx_varbind *vb = snmp_create_varbind(c.buffer, &uptime_oid); + for (uint i = 0; i < uptime_oid.n_subid; i++) STORE_U32(vb->name.ids[i], uptime_ids[i]); - snmp_varbind_ticks(vb, c.size, (current_time() TO_S) / 100); - ADVANCE(c.buffer, c.size, snmp_varbind_size(vb)); + + /* TODO: use time from last reconfiguration instead? [config->load_time] */ + btime uptime = current_time() - boot_time; + snmp_varbind_ticks(vb, c.size, (uptime TO_S) / 100); + ASSUME(snmp_test_varbind(vb)); + ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(vb)); } /* snmpTrapOID.0 oid */ struct oid trap0 = { .n_subid = 6, - .prefix = 6, + .prefix = 6, /* snmpV2 */ .include = 0, .pad = 0, }; + /* {snmpV2}.snmpModules.snmpAlarmNextIndex.snmpMIBObjects.snmpTrap.snmpTrapIOD.0 */ u32 trap0_ids[] = { 3, 1, 1, 4, 1, 0 }; struct agentx_varbind *trap_vb = snmp_create_varbind(c.buffer, &trap0); @@ -314,67 +330,18 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in STORE_U32(trap_vb->name.ids[i], trap0_ids[i]); trap_vb->type = AGENTX_OBJECT_ID; snmp_put_oid(snmp_varbind_data(trap_vb), oid); - ADVANCE(c.buffer, c.size, snmp_varbind_size(trap_vb)); + ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(trap_vb)); memcpy(c.buffer, data, size); ADVANCE(c.buffer, c.size, size); - uint s = update_packet_size(p, sk->tpos, c.buffer); + uint s = update_packet_size(p, h, c.buffer); sk_send(sk, s); #undef TRAP0_HEADER_SIZE #undef UPTIME_SIZE } -#if 0 -/* - * de_allocate_pdu - common functionality for allocation PDUs - * @p: SNMP protocol instance - * @oid: OID to allocate/deallocate - * @type: allocate/deacollcate PDU type - * @flags: type of allocation (NEW_INDEX, ANY_INDEX) - * - * This function is internal and shouldn't be used outside the SNMP module. - */ -static void -de_allocate_pdu(struct snmp_proto *p, struct oid *oid, enum agentx_pdu_types type, u8 flags) -{ - sock *sk = p->sock; - byte *buf, *pkt; - buf = pkt = sk->tbuf; - uint size = sk->tbsize; - struct snmp_pdu c; - snmp_pdu_context(&c, p->sock); - - - if (size > AGENTX_HEADER_SIZE + 0) // TODO additional size - { - struct agentx_header *h; - SNMP_CREATE(pkt, struct agentx_header, h); - snmp_blank_header(h, type); - snmp_session(p, h); - - struct agentx_varbind *vb = (struct agentx_varbind *) pkt; - - // TODO - STORE_16(vb->type, AGENTX_OBJECT_ID); - STORE(vb->oid, 0); - } -} - -void -snmp_allocate(struct snmp_proto *p, struct oid *oid, u8 flags) -{ - /* TODO - call the de_allocate_pdu() */ -} - -void -snmp_deallocate(struct snmp_proto *p, struct oid *oid, u8 flags) -{ - /* TODO - call the de_allocate_pdu() */ -} -#endif - /* * un_register_pdu - common functionality for registration PDUs * @p: SNMP protocol instance @@ -404,7 +371,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf); sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); /* conditional +4 for upper-bound (optinal field) */ uint sz = AGENTX_HEADER_SIZE + snmp_oid_size(oid) + ((bound > 1) ? 4 : 0); @@ -412,11 +379,13 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e if (c.size < sz) snmp_manage_tbuf(p, &c); - struct agentx_header *h = (struct agentx_header *) c.buffer; + struct agentx_header *h = snmp_create_tx_header(p, c.buffer); ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_header(h, type, is_instance ? AGENTX_FLAG_INSTANCE_REGISTRATION : 0); p->packet_id++; + log(L_INFO "incementing the packet_id to %u (%s)", p->packet_id, type == + AGENTX_REGISTER_PDU ? "register" : "unregister"); snmp_session(p, h); struct agentx_un_register_hdr *ur = (struct agentx_un_register_hdr *) c.buffer; @@ -439,7 +408,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e ADVANCE(c.buffer, c.size, 4); } - uint s = update_packet_size(p, sk->tpos, c.buffer); + uint s = update_packet_size(p, h, c.buffer); sk_send(sk, s); } @@ -458,6 +427,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e void snmp_register(struct snmp_proto *p, struct oid *oid, uint bound, uint index, u8 is_instance, uint contid) { + log(L_INFO "performing a registration"); un_register_pdu(p, oid, bound, index, AGENTX_REGISTER_PDU, is_instance, contid); } @@ -487,22 +457,23 @@ close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason) { sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); #define REASON_SIZE 4 if (c.size < AGENTX_HEADER_SIZE + REASON_SIZE) snmp_manage_tbuf(p, &c); - struct agentx_header *h = (struct agentx_header *) c.buffer; + struct agentx_header *h = snmp_create_tx_header(p, c.buffer); ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_blank_header(h, AGENTX_CLOSE_PDU); p->packet_id++; + log(L_INFO "incrementing packet_id %u (close)", p->packet_id); snmp_session(p, h); snmp_put_fbyte(c.buffer, (u8) reason); ADVANCE(c.buffer, c.size, 4); - uint s = update_packet_size(p, sk->tpos, c.buffer); + uint s = update_packet_size(p, h, c.buffer); sk_send(sk, s); #undef REASON_SIZE } @@ -520,25 +491,31 @@ 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) + if (size < sizeof(struct agentx_close_pdu)) { - /* The packet is malform, we reset the connnection anyway */ - TRACE(D_PACKETS, "SNMP last PDU was malformed (size)"); + TRACE(D_PACKETS, "SNMP malformed agentx-Close-PDU, closing anyway"); snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); - } - 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_set_state(p, SNMP_RESET); + return size; } + struct agentx_close_pdu *pdu = (void *) pkt; + ADVANCE(pkt, size, sizeof(struct agentx_close_pdu)); + + if (!snmp_test_close_reason(pdu->reason)) + { + TRACE(D_PACKETS, "SNMP invalid close reason"); + snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); + snmp_set_state(p, SNMP_RESET); + return sizeof(struct agentx_close_pdu); + } + + enum agentx_close_reasons reason = (enum agentx_close_reasons) pdu->reason; + TRACE(D_PACKETS, "SNMP close reason %u", reason); + snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0); snmp_set_state(p, SNMP_RESET); - return AGENTX_HEADER_SIZE; + return sizeof(struct agentx_close_pdu); } @@ -565,7 +542,6 @@ snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid * (void)p;(void)vb;(void)oid;(void)pkt_size; return 0; #if 0 - // TODO better logic if (!oid) return 0; @@ -579,89 +555,6 @@ snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid * #endif } - -#if 0 -static void UNUSED -addagentcaps_pdu(struct snmp_proto *p, struct oid *cap, char *descr, - uint descr_len) -{ - ASSUME(descr != NULL && descr_len > 0); - sock *sk = p->sock; - //byte *buf = sk->tbuf; - //uint size = sk->tbsize; - // TO-DO rename to pkt and add pkt_start - byte *buf = sk->tpos; - uint size = sk->tbuf + sk->tbsize - sk->tpos; - - if (size < AGENTX_HEADER_SIZE + snmp_oid_size(cap) + snmp_str_size_from_len(descr_len)) - { - /* TO-DO need more mem */ - return; - } - - struct agentx_header *h; - SNMP_CREATE(buf, struct agentx_header, h); - snmp_blank_header(h, AGENTX_ADD_AGENT_CAPS_PDU); - snmp_session(p, h); - ADVANCE(buf, size, AGENTX_HEADER_SIZE); - - uint in_pkt; - if (c && c->length) - { - SNMP_HAS_CONTEXT(h); - ADVANCE(buf, size, in_pkt); - } - - // memcpy(buf, cap, snmp_oid_size(cap)); - ADVANCE(buf, size, snmp_oid_size(cap)); - - in_pkt = snmp_put_nstr(buf, descr, descr_len) - buf; - ADVANCE(buf, size, in_pkt); - - // make a note in the snmp_proto structure - - //int ret = sk_send(sk, buf - sk->tbuf); - sk_send(sk, buf - sk->tpos); -} - -static void UNUSED -removeagentcaps_pdu(struct snmp_proto *p, struct oid *cap) -{ - sock *sk = p->sock; - - //byte *buf = sk->tbuf; - //uint size = sk->tbsize; - // TO-DO rename to pkt and add pkt_start - byte *buf = sk->tpos; - uint size = sk->tbuf + sk->tbsize - sk->tpos; - - if (size < AGENTX_HEADER_SIZE + snmp_oid_size(cap)) - { - /* TO-DO need more mem */ - return; - } - - struct agentx_header *h; - SNMP_CREATE(buf, struct agentx_header, h); - snmp_session(p, h); - ADVANCE(buf, size, AGENTX_HEADER_SIZE); - - uint in_pkt; - if (c && c->length) - { - SNMP_HAS_CONTEXT(h); - ADVANCE(buf, size, in_pkt); - } - - memcpy(buf, cap, snmp_oid_size(cap)); - ADVANCE(buf, size, snmp_oid_size(cap)); - - // update state in snmp_proto structure - - sk_send(sk, buf - sk->tpos); -} -#endif - /* * refresh_ids - Copy current ids from packet to protocol * @p: SNMP protocol instance @@ -672,6 +565,7 @@ 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); + //log(L_INFO "loading packet_id %u from header %p", p->packet_id, h); } /* @@ -699,7 +593,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); + snmp_pdu_context(p, &c, sk); if (c.size < AGENTX_HEADER_SIZE) snmp_manage_tbuf(p, &c); @@ -722,7 +616,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) while (size > 0 && all_possible) { vb = (void *) pkt; - sz = snmp_varbind_size(vb, 0); + sz = snmp_varbind_size(vb, size); if (sz > size) /* wait for more data to arive */ @@ -742,7 +636,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) all_possible = 0; break; } - ADVANCE(pkt, size, snmp_varbind_size(vb, 0)); + ADVANCE(pkt, size, snmp_varbind_size(vb, size)); // TODO remove the mb_alloc() in prefixize() struct oid *work = snmp_prefixize(p, &vb->name); @@ -752,24 +646,23 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) } mb_free(tr); #endif - s = update_packet_size(p, sk->tpos, c.buffer); + s = update_packet_size(p, h, c.buffer); 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); + response_err_ind(p, res, c.error, c.index + 1); snmp_error(p); } else if (all_possible) { - response_err_ind(res, AGENTX_RES_NO_ERROR, 0); + response_err_ind(p, 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); + /* This is a recoverable error, we do not need to reset the connection */ + //response_err_ind(p, res, AGENTX_RES_RESOURCE_UNAVAILABLE, c.index + 1); + response_err_ind(p, res, AGENTX_RES_NOT_WRITABLE, c.index + 1); } sk_send(sk, s); @@ -777,7 +670,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) } /* - * parse_set_pdu - common functionality for commit set and undo set PDUs + * 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 * @size: number of bytes received from a socket @@ -786,9 +679,8 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) * Return number of bytes parsed from RX-buffer. */ static uint -parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agentx_response_errs err) +parse_sets_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); @@ -804,7 +696,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen } struct snmp_pdu c; - snmp_pdu_context(&c, p->sock); + snmp_pdu_context(p, &c, p->sock); if (c.size < sizeof(struct agentx_response)) snmp_manage_tbuf(p, &c); @@ -823,7 +715,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen } TRACE(D_PACKETS, "SNMP received set PDU with error %u", c.error); - response_err_ind(r, c.error, 0); + response_err_ind(p, r, c.error, 0); sk_send(p->sock, AGENTX_HEADER_SIZE); /* Reset the connection on unrecoverable error */ @@ -847,7 +739,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); + return parse_sets_pdu(p, pkt, size, AGENTX_RES_COMMIT_FAILED); } /* @@ -864,7 +756,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); + return parse_sets_pdu(p, pkt, size, AGENTX_RES_UNDO_FAILED); } /* @@ -899,6 +791,22 @@ parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) 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 @@ -911,12 +819,21 @@ parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size) static uint parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip) { + /* TX-buffer free space */ + ASSERT(snmp_is_active(p)); + if (!space_for_response(p->sock)) + return 0; + + ASSERT(snmp_is_active(p)); if (size < AGENTX_HEADER_SIZE) return 0; struct agentx_header *h = (void *) pkt; uint pkt_size = LOAD_U32(h->payload); + if (pkt_size > SNMP_PKT_SIZE_MAX) + return simple_response(p, AGENTX_RES_GEN_ERR, 0); + /* 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 @@ -924,9 +841,11 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip) if (h->type == AGENTX_RESPONSE_PDU) return parse_response(p, pkt, size); + ASSERT(snmp_is_active(p)); if (p->state != SNMP_CONN || p->session_id != LOAD_U32(h->session_id)) { + // TODO: resolve issues connected to partial processed packets struct agentx_header copy = { .session_id = p->session_id, .transaction_id = p->transaction_id, @@ -939,23 +858,25 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip) p->session_id = copy.session_id; p->transaction_id = copy.transaction_id; p->packet_id = copy.packet_id; + log(L_INFO "restoring packet_id %u from temporal state", p->packet_id); - // TODO: fix the parsed size for possibly malitious packets - // this could be broken when sender sends the packet in two parts - // -> size < pkt_size; - // maliciously large pkt_size -> breaks the size >= pkt_size - // maybe move this test down to parse_*() functions - return MIN(size, pkt_size + AGENTX_HEADER_SIZE); + /* + * After unexpected state, we simply reset the session + * only sending the agentx-Response-PDU. + */ + snmp_set_state(p, SNMP_RESET); + return 0; } + ASSERT(snmp_is_active(p)); if (h->flags & AGENTX_NON_DEFAULT_CONTEXT) { - // TODO: shouldn't we return a parseError or genError for - // packets that mustn't have a non-default context ?? + // TODO: add non-default context support 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); + /* We always accept the packet length as correct, up to set limit */ + // TODO limit + return pkt_size + AGENTX_HEADER_SIZE; } refresh_ids(p, h); @@ -982,10 +903,10 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip) return parse_cleanup_set_pdu(p, pkt, size); default: + /* We reset the connection for malformed packet (Unknown packet type) */ 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; + snmp_set_state(p, SNMP_RESET); + return 0; } } @@ -1001,21 +922,22 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip) static uint parse_response(struct snmp_proto *p, byte *res, uint size) { + log(L_WARN "parse response"); if (size < sizeof(struct agentx_response)) return 0; struct agentx_response *r = (void *) res; - struct agentx_header *h = &r->h; + struct agentx_header *h = (void *) r; // todo reject not compiled byte order uint pkt_size = LOAD_U32(h->payload); 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: + TRACE(D_PACKETS, "SNMP received agetnx-Response-PDU"); do_response(p, res, size); break; @@ -1024,6 +946,7 @@ parse_response(struct snmp_proto *p, byte *res, uint size) case AGENTX_RES_REQUEST_DENIED: case AGENTX_RES_UNKNOWN_REGISTER: // TODO: more direct path to mib-specifiec code + TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error); snmp_register_ack(p, r, size); break; @@ -1070,7 +993,7 @@ static void do_response(struct snmp_proto *p, byte *buf, uint size) { struct agentx_response *r = (void *) buf; - struct agentx_header *h = &r->h; + struct agentx_header *h = (void *) r; /* TODO make it asynchronous for better speed */ switch (p->state) @@ -1161,9 +1084,15 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, case SNMP_SEARCH_END_OF_VIEW:; uint sz = snmp_varbind_hdr_size_from_oid(o_start); - if (c->size < sz) + if (c->size < sz && c->size >= sizeof(struct agentx_varbind)) + { + struct agentx_varbind *vb_null = snmp_create_varbind_null(c->buffer); + ADVANCE(c->buffer, c->size, snmp_varbind_size_unsafe(vb_null)); + c->error = AGENTX_RES_GEN_ERROR; + return 0; + } + else if (c->size < sz) { - /* TODO create NULL varbind */ c->error = AGENTX_RES_GEN_ERROR; return 0; } @@ -1199,10 +1128,7 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, } if (c->size < snmp_varbind_hdr_size_from_oid(o_start)) - { - // TODO FIXME this is a bit tricky as we need to renew all TX buffer pointers snmp_manage_tbuf(p, c); - } vb = snmp_create_varbind(c->buffer, o_start); vb->type = AGENTX_END_OF_MIB_VIEW; @@ -1286,28 +1212,44 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, * Return number of bytes in TX-buffer (including header size). */ static inline uint -update_packet_size(struct snmp_proto *p, const byte *start, byte *end) +update_packet_size(struct snmp_proto *p, struct agentx_header *start, byte *end) { - struct agentx_header *h = (void *) p->sock->tpos; - size_t s = snmp_pkt_len(start, end); - STORE_U32(h->payload, s); + 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. + * 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 agentx_response *res, enum agentx_response_errs err, u16 ind) +response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind) { STORE_U32(res->error, (u16) err); - if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_PARSE_ERROR) + // TODO deal with auto-incrementing of snmp_pdu context c.ind + if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR) + { + TRACE(D_PACKETS, "Last PDU resulted in error %u", err); STORE_U32(res->index, ind); + TRACE(D_PACKETS, "Storing packet size %u (was %u)", sizeof(struct agentx_response) - AGENTX_HEADER_SIZE, LOAD_U32(res->h.payload)); + STORE_U32(res->h.payload, + sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); + } + else if (err == AGENTX_RES_GEN_ERROR) + { + TRACE(D_PACKETS, "Last PDU resulted in error %u", err); + STORE_U32(res->index, 0); + TRACE(D_PACKETS, "Storing packet size %u (was %u)", sizeof(struct agentx_response) - AGENTX_HEADER_SIZE, LOAD_U32(res->h.payload)); + STORE_U32(res->h.payload, + sizeof(struct agentx_response) - AGENTX_HEADER_SIZE); + } else STORE_U32(res->index, 0); } @@ -1337,8 +1279,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s sock *sk = p->sock; struct snmp_pdu c; - snmp_pdu_context(&c, sk); - // TODO better handling of endianness + snmp_pdu_context(p, &c, sk); /* * Get-Bulk processing stops if all the varbind have type END_OF_MIB_VIEW @@ -1355,7 +1296,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s { c.error = AGENTX_RES_PARSE_ERROR; c.index = 0; - ret = MIN(size, pkt_size + AGENTX_HEADER_SIZE); + ret = pkt_size + AGENTX_HEADER_SIZE; goto error; } @@ -1373,12 +1314,6 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s }; } - if (c.size < sizeof(struct agentx_response)) - { - snmp_manage_tbuf(p, &c); - // TODO renew pkt, pkt_start pointers - } - struct agentx_response *response_header = prepare_response(p, &c); while (c.error == AGENTX_RES_NO_ERROR && size > 0 && pkt_size > 0) @@ -1403,7 +1338,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s */ if (sz > size && c.index > 0) { - goto partial; /* send already processed part */ + goto partial; } else if (sz > size) { @@ -1446,6 +1381,9 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s o_start = snmp_prefixize(p, o_start_b); o_end = snmp_prefixize(p, o_end_b); + ASSERT(o_start); + ASSERT(o_end); + if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(o_start, o_end) > 0) { c.error = AGENTX_RES_GEN_ERROR; @@ -1505,21 +1443,23 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s /* send the constructed packet */ struct agentx_response *res; - if (p->last_header) + if (snmp_is_partial(p)) { - res = p->last_header; - p->last_header = NULL; - p->last_size = 0; + snmp_log("snmp_is_partial() packet_id %u last_pkt_id %u [proto packet_id] %u", LOAD_U32(res->h.packet_id), p->last_pkt_id, p->packet_id); + res = SKIP_BACK(struct agentx_response, h, snmp_get_header(p)); + ASSERT(LOAD_U32(res->h.packet_id) == p->last_pkt_id); + STORE_U32(res->h.packet_id, p->last_pkt_id); + snmp_unset_header(p); } else res = response_header; /* We update the error, index pair on the beginning of the packet. */ - response_err_ind(res, c.error, c.index + 1); - uint s = update_packet_size(p, (byte *) res, c.buffer); + response_err_ind(p, res, c.error, c.index + 1); + uint s = update_packet_size(p, &res->h, c.buffer); /* We send the message in TX-buffer. */ - p->last_header = NULL; + snmp_unset_header(p); sk_send(sk, s); // TODO think through the error state @@ -1529,31 +1469,28 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s partial: /* need to tweak RX buffer packet size */ - STORE_U32(h->payload, pkt_size); + //STORE_U32(h->payload, pkt_size); *skip = AGENTX_HEADER_SIZE; - p->last_header = h; - p->last_size = c.buffer - sk->tpos; + snmp_set_header(p, &response_header->h, &c); /* number of bytes parsed from RX-buffer */ ret = pkt - pkt_start; + /* update the packet length such that the used bytes are not included */ + STORE_U32(h->payload, LOAD_U32(h->payload) - ret + AGENTX_HEADER_SIZE); goto free; wait: p->packet_id--; /* we did not use the packetID */ + log(L_INFO "decrementing back the packet_id to %u", p->packet_id); ret = 0; goto free; error: + TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c.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); @@ -1570,12 +1507,12 @@ free: void snmp_start_subagent(struct snmp_proto *p) { + ASSUME(p->state == SNMP_OPEN); + /* blank oid means unsupported */ struct oid *blank = snmp_oid_blank(p); open_pdu(p, blank); - p->state = SNMP_OPEN; - mb_free(blank); } @@ -1600,30 +1537,37 @@ snmp_stop_subagent(struct snmp_proto *p) int snmp_rx(sock *sk, uint size) { + log(L_INFO "snmp_rx with size %u", size); struct snmp_proto *p = sk->data; byte *pkt_start = sk->rbuf; byte *end = pkt_start + size; + byte *last_pkt = pkt_start; + /* * In some cases we want to save the header for future parsing, skip is number * of bytes that should not be overriden by memmove() */ uint skip = 0; - while (end >= pkt_start + AGENTX_HEADER_SIZE && skip == 0) + while (snmp_is_active(p) && end >= pkt_start + AGENTX_HEADER_SIZE && skip == 0) { uint parsed_len = parse_pkt(p, pkt_start, size, &skip); if (parsed_len == 0) break; + last_pkt = pkt_start; pkt_start += parsed_len; size -= parsed_len; } - /* Incomplete packets */ + if (!snmp_is_active(p)) + return 1; + if (skip != 0 || pkt_start != end) { + memmove(sk->rbuf, last_pkt, skip); /* maybe no op */ memmove(sk->rbuf + skip, pkt_start, size); sk->rpos = sk->rbuf + size + skip; return 0; @@ -1632,6 +1576,28 @@ snmp_rx(sock *sk, uint size) return 1; } +/* + * snmp_tx - handle TX-buffer + * @sk: communication socket owned by SNMP protocol instance + * + * The snmp_tx hook is used only to delay the processing in cases we don't have + * enough space in TX-buffer. Therefore we simply call the snmp_rx hook. + */ +void +snmp_tx(sock *sk) +{ + log(L_INFO "snmp_tx()"); + /* We still not have enough space */ + if (!space_for_response(sk)) + return; + + /* There is nothing to process, no bytes in RX-buffer */ + if (sk_tx_buffer_empty(sk)) + return; + + snmp_rx(sk, sk->tpos - sk->tbuf); +} + /* * snmp_ping - send an agentx-Ping-PDU @@ -1640,30 +1606,40 @@ snmp_rx(sock *sk, uint size) 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, sk); + snmp_pdu_context(p, &c, sk); if (c.size < AGENTX_HEADER_SIZE) return; - int unused = (sk->tbsize - (sk->tpos - sk->tbuf)) - (p->last_size + AGENTX_HEADER_SIZE); - if (p->last_header && unused >= 0) - { - - } - else if (p->last_header) + int unused = sk->tbuf + sk->tbsize - c.buffer; + if (snmp_is_partial(p) && unused >= AGENTX_HEADER_SIZE) { + /* We use the start of unsent TX-buffer to fit in the agentx-Ping-PDU */ + memmove(sk->tpos + AGENTX_HEADER_SIZE, sk->tpos, p->last_size); + p->header_offset += AGENTX_HEADER_SIZE; } + else if (snmp_is_partial(p)) + /* Not enough space inside the buffer */ + return; - struct agentx_header *h = (struct agentx_header *) c.buffer; + /* we do not use the snmp_create_tx_header() because of side effects */ + struct agentx_header *h = (void *) sk->tpos; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_blank_header(h, AGENTX_PING_PDU); p->packet_id++; + log(L_INFO "incrementing packet_id to %u (ping)", p->packet_id); snmp_session(p, h); + + if (snmp_is_partial(p)) // TODO remove me + snmp_log("snmp_ping() packet_id %u last_pkt_id %u [proto packet_id] ))%u", LOAD_U32(h->packet), p->last_pkt_id, p->packet_id); - /* sending only header -> pkt - buf */ - uint s = update_packet_size(p, sk->tpos, c.buffer); + /* sending only header */ + uint s = update_packet_size(p, h, (byte *) h + AGENTX_HEADER_SIZE); sk_send(sk, s); } @@ -1777,7 +1753,7 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_ struct oid * snmp_prefixize(struct snmp_proto *proto, const struct oid *oid) { - ASSERT(oid != NULL); + ASSUME(oid != NULL); if (snmp_is_oid_empty(oid)) { @@ -1870,13 +1846,11 @@ 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; + log(L_INFO "snmp_manage_tbuf()"); sk_set_tbsize(sk, sk->tbsize + 2048); - // XXX buffer snmp_pdu pointer c->size += 2048; - */ } /* @@ -1889,11 +1863,11 @@ snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c) static struct agentx_response * prepare_response(struct snmp_proto *p, struct snmp_pdu *c) { - if (p->last_header) - return p->last_header; + if (snmp_is_partial(p)) + return (struct agentx_response *) snmp_get_header(p); struct agentx_response *r = (void *) c->buffer; - struct agentx_header *h = &r->h; + struct agentx_header *h = snmp_create_tx_header(p, (byte *) r); snmp_blank_header(h, AGENTX_RESPONSE_PDU); snmp_session(p, h); diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index 9a263a28..c91206ea 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -36,8 +36,6 @@ enum SNMP_CLASSES { SNMP_CLASS_END, }; -#define BGP4_VERSIONS ((char[]) { 0x10 }) - enum agentx_type { AGENTX_INTEGER = 2, AGENTX_OCTET_STRING = 4, @@ -52,6 +50,8 @@ enum agentx_type { AGENTX_NO_SUCH_OBJECT = 128, AGENTX_NO_SUCH_INSTANCE = 129, AGENTX_END_OF_MIB_VIEW = 130, + + AGENTX_INVALID = -1, } PACKED; enum snmp_search_res { @@ -158,7 +158,8 @@ struct agentx_header { u32 payload; /* payload_length of the packet without header */ }; -#define AGENTX_HEADER_SIZE sizeof(struct agentx_header) +#define AGENTX_HEADER_SIZE 20 +STATIC_ASSERT(AGENTX_HEADER_SIZE == sizeof(struct agentx_header)); struct oid { u8 n_subid; @@ -176,10 +177,15 @@ struct agentx_varbind { /* AgentX variable binding data optionaly here */ }; -/* this does not work */ struct agentx_search_range { - struct oid start; - struct oid end; + struct oid *start; + struct oid *end; +}; + +/* AgentX Octet String */ +struct agentx_octet_str { + u32 length; + byte data[0]; }; struct agentx_getbulk { @@ -194,9 +200,13 @@ struct agentx_response { u16 index; }; +STATIC_ASSERT(4 + 2 + 2 + AGENTX_HEADER_SIZE == sizeof(struct agentx_response)); + struct agentx_close_pdu { struct agentx_header h; u8 reason; + u8 pad1; + u16 pad2; }; struct agentx_un_register_hdr { @@ -279,7 +289,7 @@ enum agentx_response_errs { AGENTX_RES_PROCESSING_ERR = 268, /* processingError */ } PACKED; -/* SNMP PDU buffer info */ +/* SNMP PDU TX-buffer info */ struct snmp_pdu { byte *buffer; /* pointer to buffer */ uint size; /* unused space in buffer */ @@ -287,6 +297,15 @@ struct snmp_pdu { u32 index; /* index on which the error was found */ }; +struct snmp_packet_info { + node n; + u8 type; // enum type + u32 session_id; + u32 transaction_id; + u32 packet_id; + void *data; +}; + #if 0 struct agentx_alloc_context { u8 is_instance; /* flag INSTANCE_REGISTRATION */