Ejemplo n.º 1
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param p points to pbuf chain of SNMP message (UDP payload)
 * @param ofs points to first octet of SNMP message
 * @param pdu_len the length of the UDP payload
 * @param ofs_ret returns the ofset of the variable bindings
 * @param m_stat points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_ARG SNMP header is either malformed or rejected
 */
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
    err_t derr;
    u16_t len, ofs_base;
    u8_t  len_octets;
    u8_t  type;
    s32_t version;

    ofs_base = ofs;
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) ||
            (pdu_len != (1 + len_octets + len)) ||
            (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (version) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    if (version != 0) {
        /* not version 1 */
        snmp_inc_snmpinbadversions();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) {
        /* can't decode or no octet string (community) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
    if (derr != ERR_OK) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* add zero terminator */
    len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
    m_stat->community[len] = 0;
    m_stat->com_strlen = (u8_t)len;
    if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) {
        /** @todo: move this if we need to check more names */
        snmp_inc_snmpinbadcommunitynames();
        snmp_authfail_trap();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if (derr != ERR_OK) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    switch(type) {
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
            /* GetRequest PDU */
            snmp_inc_snmpingetrequests();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
            /* GetNextRequest PDU */
            snmp_inc_snmpingetnexts();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
            /* GetResponse PDU */
            snmp_inc_snmpingetresponses();
            derr = ERR_ARG;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
            /* SetRequest PDU */
            snmp_inc_snmpinsetrequests();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
            /* Trap PDU */
            snmp_inc_snmpintraps();
            derr = ERR_ARG;
            break;
        default:
            snmp_inc_snmpinasnparseerrs();
            derr = ERR_ARG;
            break;
    }
    if (derr != ERR_OK) {
        /* unsupported input PDU for this agent (no parse error) */
        return ERR_ARG;
    }
    m_stat->rt = type & 0x1F;
    ofs += (1 + len_octets);
    if (len != (pdu_len - (ofs - ofs_base))) {
        /* decoded PDU length does not equal actual payload length */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (request ID) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (error-status) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* must be noError (0) for incoming requests.
       log errors for mib-2 completeness and for debug purposes */
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    switch (m_stat->error_status) {
        case SNMP_ES_TOOBIG:
            snmp_inc_snmpintoobigs();
            break;
        case SNMP_ES_NOSUCHNAME:
            snmp_inc_snmpinnosuchnames();
            break;
        case SNMP_ES_BADVALUE:
            snmp_inc_snmpinbadvalues();
            break;
        case SNMP_ES_READONLY:
            snmp_inc_snmpinreadonlys();
            break;
        case SNMP_ES_GENERROR:
            snmp_inc_snmpingenerrs();
            break;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (error-index) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* must be 0 for incoming requests.
       decode anyway to catch bad integers (and dirty tricks) */
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    *ofs_ret = ofs;
    return ERR_OK;
}
Ejemplo n.º 2
0
static err_t
snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
    err_t derr;
    u16_t len, vb_len;
    u8_t  len_octets;
    u8_t type;

    /* variable binding list */
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
    if ((derr != ERR_OK) ||
            (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets);

    /* start with empty list */
    m_stat->invb.count = 0;
    m_stat->invb.head = NULL;
    m_stat->invb.tail = NULL;

    while (vb_len > 0) {
        struct snmp_obj_id oid, oid_value;
        struct snmp_varbind *vb;

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if ((derr != ERR_OK) ||
                (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
                (len == 0) || (len > vb_len)) {
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets);
        vb_len -= (1 + len_octets);

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) {
            /* can't decode object name length */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
        if (derr != ERR_OK) {
            /* can't decode object name */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets + len);
        vb_len -= (1 + len_octets + len);

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if (derr != ERR_OK) {
            /* can't decode object value length */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }

        switch (type) {
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
                vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
                if (vb != NULL) {
                    s32_t *vptr = (s32_t*)vb->value;

                    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
                vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
                if (vb != NULL) {
                    u32_t *vptr = (u32_t*)vb->value;

                    derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
                LWIP_ASSERT("invalid length", len <= 0xff);
                vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
                if (vb != NULL) {
                    derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
                vb = snmp_varbind_alloc(&oid, type, 0);
                if (vb != NULL) {
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                    derr = ERR_OK;
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
                derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
                if (derr == ERR_OK) {
                    vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
                    if (vb != NULL) {
                        u8_t i = oid_value.len;
                        s32_t *vptr = (s32_t*)vb->value;

                        while(i > 0) {
                            i--;
                            vptr[i] = oid_value.id[i];
                        }
                        snmp_varbind_tail_add(&m_stat->invb, vb);
                        derr = ERR_OK;
                    } else {
                        derr = ERR_ARG;
                    }
                }
                break;
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
                if (len == 4) {
                    /* must be exactly 4 octets! */
                    vb = snmp_varbind_alloc(&oid, type, 4);
                    if (vb != NULL) {
                        derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
                        snmp_varbind_tail_add(&m_stat->invb, vb);
                    } else {
                        derr = ERR_ARG;
                    }
                } else {
                    derr = ERR_ARG;
                }
                break;
            default:
                derr = ERR_ARG;
                break;
        }
        if (derr != ERR_OK) {
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets + len);
        vb_len -= (1 + len_octets + len);
    }

    if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) {
        snmp_add_snmpintotalsetvars(m_stat->invb.count);
    } else {
        snmp_add_snmpintotalreqvars(m_stat->invb.count);
    }

    *ofs_ret = ofs;
    return ERR_OK;
}
Ejemplo n.º 3
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param p points to pbuf chain of SNMP message (UDP payload)
 * @param ofs points to first octet of SNMP message
 * @param pdu_len the length of the UDP payload
 * @param ofs_ret returns the ofset of the variable bindings
 * @param m_stat points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_ARG SNMP header is either malformed or rejected
 */
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, ofs_base;
  u8_t  len_octets;
  u8_t  type;
  s32_t version;

  ofs_base = ofs;
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) ||
      (pdu_len != (1 + len_octets + len)) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (version) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  if (version != 0)
  {
    /* not version 1 */
    snmp_inc_snmpinbadversions();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
  {
    /* can't decode or no octet string (community) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* add zero terminator */
  len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
  m_stat->community[len] = 0;
  m_stat->com_strlen = (u8_t)len;
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
#if SNMP_COMMUNITY_EXT
  if (strncmp(snmp_community_write, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
  {
    /* community does not match the write-access community, check if this is a SetRequest */
    if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ))
    {
      /* wrong community for SetRequest PDU */
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
#else /* SNMP_COMMUNITY_EXT */
  {
#endif /* SNMP_COMMUNITY_EXT */
    if (strncmp(snmp_community, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
    {
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
  }
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch(type)
  {
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
      /* GetRequest PDU */
      snmp_inc_snmpingetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
      /* GetNextRequest PDU */
      snmp_inc_snmpingetnexts();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
      /* GetResponse PDU */
      snmp_inc_snmpingetresponses();
      derr = ERR_ARG;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
      /* SetRequest PDU */
      snmp_inc_snmpinsetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
      /* Trap PDU */
      snmp_inc_snmpintraps();
      derr = ERR_ARG;
      break;
    default:
      snmp_inc_snmpinasnparseerrs();
      derr = ERR_ARG;
      break;
  }
  if (derr != ERR_OK)
  {
    /* unsupported input PDU for this agent (no parse error) */
    return ERR_ARG;
  }
  m_stat->rt = type & 0x1F;
  ofs += (1 + len_octets);
  if (len != (pdu_len - (ofs - ofs_base)))
  {
    /* decoded PDU length does not equal actual payload length */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (request ID) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-status) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be noError (0) for incoming requests.
     log errors for mib-2 completeness and for debug purposes */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch (m_stat->error_status)
  {
    case SNMP_ES_NOERROR:
      /* nothing to do */
      break;
    case SNMP_ES_TOOBIG:
      snmp_inc_snmpintoobigs();
      break;
    case SNMP_ES_NOSUCHNAME:
      snmp_inc_snmpinnosuchnames();
      break;
    case SNMP_ES_BADVALUE:
      snmp_inc_snmpinbadvalues();
      break;
    case SNMP_ES_READONLY:
      snmp_inc_snmpinreadonlys();
      break;
    case SNMP_ES_GENERROR:
      snmp_inc_snmpingenerrs();
      break;
    default:
      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check(): unknown error_status: %d\n", (int)m_stat->error_status));
      break;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-index) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be 0 for incoming requests.
     decode anyway to catch bad integers (and dirty tricks) */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  *ofs_ret = ofs;
  return ERR_OK;
}

static err_t
snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, vb_len;
  u8_t  len_octets;
  u8_t type;

  /* variable binding list */
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
  if ((derr != ERR_OK) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);

  /* start with empty list */
  m_stat->invb.count = 0;
  m_stat->invb.head = NULL;
  m_stat->invb.tail = NULL;

  while (vb_len > 0)
  {
    struct snmp_obj_id oid, oid_value;
    struct snmp_varbind *vb;

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) ||
        (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
        (len == 0) || (len > vb_len))
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets);
    vb_len -= (1 + len_octets);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
    {
      /* can't decode object name length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
    if (derr != ERR_OK)
    {
      /* can't decode object name */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if (derr != ERR_OK)
    {
      /* can't decode object value length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }

    switch (type)
    {
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
        vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
        if (vb != NULL)
        {
          s32_t *vptr = (s32_t*)vb->value;

          derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
        vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
        if (vb != NULL)
        {
          u32_t *vptr = (u32_t*)vb->value;

          derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
        LWIP_ASSERT("invalid length", len <= 0xff);
        vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
        if (vb != NULL)
        {
          derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
        vb = snmp_varbind_alloc(&oid, type, 0);
        if (vb != NULL)
        {
          snmp_varbind_tail_add(&m_stat->invb, vb);
          derr = ERR_OK;
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
        if (derr == ERR_OK)
        {
          vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
          if (vb != NULL)
          {
            u8_t i = oid_value.len;
            s32_t *vptr = (s32_t*)vb->value;

            while(i > 0)
            {
              i--;
              vptr[i] = oid_value.id[i];
            }
            snmp_varbind_tail_add(&m_stat->invb, vb);
            derr = ERR_OK;
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
        if (len == 4)
        {
          /* must be exactly 4 octets! */
          vb = snmp_varbind_alloc(&oid, type, 4);
          if (vb != NULL)
          {
            derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
            snmp_varbind_tail_add(&m_stat->invb, vb);
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      default:
        derr = ERR_ARG;
        break;
    }
    if (derr != ERR_OK)
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);
  }

  if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
  {
    snmp_add_snmpintotalsetvars(m_stat->invb.count);
  }
  else
  {
    snmp_add_snmpintotalreqvars(m_stat->invb.count);
  }

  *ofs_ret = ofs;
  return ERR_OK;
}