Ejemplo n.º 1
0
snmp_vb_enumerator_err_t
snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind)
{
  struct snmp_asn1_tlv tlv;
  u16_t  varbind_len;
  err_t  err;
  
  if (enumerator->pbuf_stream.length == 0)
  {
    return SNMP_VB_ENUMERATOR_ERR_EOVB;
  }
  enumerator->varbind_count++;

  /* decode varbind itself (parent container of a varbind) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
  varbind_len = tlv.value_len;

  /* decode varbind name (object id) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
   
  VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
  varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);

  /* decode varbind value (object id) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
  varbind->type = tlv.type;

  /* shall the value be decoded ? */
  if (varbind->value != NULL) {
    switch (varbind->type) {
      case SNMP_ASN1_TYPE_INTEGER:
        VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value));
        varbind->value_len = sizeof(s32_t*);
        break;
      case SNMP_ASN1_TYPE_COUNTER:
      case SNMP_ASN1_TYPE_GAUGE:
      case SNMP_ASN1_TYPE_TIMETICKS:
        VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
        varbind->value_len = sizeof(u32_t*);
        break;
      case SNMP_ASN1_TYPE_OCTET_STRING:
      case SNMP_ASN1_TYPE_OPAQUE:
        err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
        if (err == ERR_MEM) {
          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
        }
        VB_PARSE_ASSERT(err == ERR_OK);
        break;
      case SNMP_ASN1_TYPE_NULL:
        varbind->value_len = 0;
        break;
      case SNMP_ASN1_TYPE_OBJECT_ID:
        /* misuse tlv.length_len as OID_length transporter */
        err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
        if (err == ERR_MEM) {
          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
        }
        VB_PARSE_ASSERT(err == ERR_OK);
        varbind->value_len = tlv.length_len * sizeof(u32_t);
        break;
      case SNMP_ASN1_TYPE_IPADDR:
        if (tlv.value_len == 4) {
          /* must be exactly 4 octets! */
          VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
        } else {
          VB_PARSE_ASSERT(0);
        }
        break;
      case SNMP_ASN1_TYPE_COUNTER64:
        VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
        varbind->value_len = 2 * sizeof(u32_t*);
        break;
      default:
        VB_PARSE_ASSERT(0);
        break;
    }
  } else {
    snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
    varbind->value_len = tlv.value_len;
  }

  return SNMP_VB_ENUMERATOR_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;
}