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 */