diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index 25280329..85fcd797 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -8,7 +8,39 @@ */ /** - * Simple Network Management Protocol State Machine + * DOC: Simple Network Management Protocol + * + * The SNMP protocol is divided into several parts: |snmp.c| which implements + * the BIRD intergration, |subagent.c| contains functions for creating and + * parsing packets, |bgp_mib.c| takes care of the bgp MIB subtree of standard + * BGP4-MIB and |snmp_utils.c| which is collections of helper functions for + * working with OIDs, VarBinds. + * + * Althrough called SNMP the BIRD does not implement SNMP directly but acts as + * an AgentX subagent. AgentX subagent connects to AgentX master agent that + * processes incomming SNMP requests and passes them down to the correct + * subagent. Therefore you need also a running master agent somewhere. + * Advantages of this design are that you are capable of doing aggregation of + * statuses of multiple BIRDs at the master agent level and much simpler + * implementation. + * + * Before any of the SNMP request could be processed, the SNMP need to + * established AgentX session with the master agent and need to register all + * subtrees to make them accessible from the master agent. The establishement of + * the of session is handled by snmp_start(), snmp_start_locked() and + * snmp_start_subagent(). Then we register all MIBs from configuration in + * snmp_register_mibs(). + * + * The AgentX request are handled only during MIB subtree registrations and + * after then on established session (in states SNMP_REGISTER and SNMP_CONN, see + * below). It is also guaranteed that no request is received before MIB subtree + * registration because the specific subagent is not authoratitave and also the + * master agent has no info about MIB subtree supported by subagent. The AgentX + * requests are handled by function snmp_rx() in |subagent.c|. + * + * + * + * SNMP State Machine * * States with main transitions * diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 50900d1c..804cfa1e 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -28,23 +28,34 @@ */ /** + * + * + * + * * Handling of malformed packet: - * When we find an error in PDU data, we create and send a response with error - * defined by the RFC. We await until the packet is send and then we close the - * communication socket. This also 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() - * to be called. The socket's tx_hook is called when the TX-buffer is empty, - * meaning our response (agentx-Response-PDU) was send. + * + * When we find an error in PDU data, we create and send a response with error + * defined by the RFC. We await until the packet is send and then we close the + * 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() + * to be called. The socket's tx_hook is called when the TX-buffer is empty, + * meaning our response (agentx-Response-PDU) was send. * * - * Partial parsing - * It may happen that we received only staring part of some PDU from the - * communication socket. In most cases if we recognize this situation we - * immediately return, waiting for rest of the PDU to arrive. But for packets - * like agentx-Get-PDU, agentx-GetNext-PDU and agentx-GetBulk-PDU it could be - * costly as they could hold many VarBinds. In these cases we process. + * Partial parsing: + * + * It may happen that we received only staring part of some PDU from the + * communication socket. In most cases, if we recognize this situation we + * immediately return, waiting for rest of the PDU to arrive. But for packets + * like agentx-Get-PDU, agentx-GetNext-PDU and agentx-GetBulk-PDU it could be + * costly as they could hold many VarBinds. We don't want to process these + * packet twice because it is a lot work. We parse all VarBinds until we hit the + * first incomplete one. The logic behind this is to release as much as + * possible space from receive buffer. When we hit the first incomplete VarBind, + * we store information about the parsing state and move the header inside the + * receive buffer. * * Transmit packet context * @@ -1498,6 +1509,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s { res = p->last_header; p->last_header = NULL; + p->last_size = 0; } else res = response_header; @@ -1520,6 +1532,7 @@ partial: STORE_U32(h->payload, pkt_size); *skip = AGENTX_HEADER_SIZE; p->last_header = h; + p->last_size = c.buffer - sk->tpos; /* number of bytes parsed from RX-buffer */ ret = pkt - pkt_start; @@ -1632,7 +1645,16 @@ snmp_ping(struct snmp_proto *p) snmp_pdu_context(&c, sk); if (c.size < AGENTX_HEADER_SIZE) - snmp_manage_tbuf(p, &c); + 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) + { + } struct agentx_header *h = (struct agentx_header *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);