Exemplo n.º 1
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;
}
Exemplo n.º 2
0
/**
 * Service an internal or external event for SNMP GET.
 *
 * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
 * @param msg_ps points to the assosicated message process state
 */
static void
snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
{
    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));

    if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) {
        struct mib_external_node *en;
        struct snmp_name_ptr np;

        /* get_object_def() answer*/
        en = msg_ps->ext_mib_node;
        np = msg_ps->ext_name_ptr;

        /* translate answer into a known lifeform */
        en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
        if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
                (msg_ps->ext_object_def.access & MIB_ACCESS_READ)) {
            msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
            en->get_value_q(request_id, &msg_ps->ext_object_def);
        } else {
            en->get_object_def_pc(request_id, np.ident_len, np.ident);
            /* search failed, object id points to unknown object (nosuchname) */
            snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
        }
    } else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) {
        struct mib_external_node *en;
        struct snmp_varbind *vb;

        /* get_value() answer */
        en = msg_ps->ext_mib_node;

        /* allocate output varbind */
        vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
        if (vb != NULL) {
            vb->next = NULL;
            vb->prev = NULL;

            /* move name from invb to outvb */
            vb->ident = msg_ps->vb_ptr->ident;
            vb->ident_len = msg_ps->vb_ptr->ident_len;
            /* ensure this memory is refereced once only */
            msg_ps->vb_ptr->ident = NULL;
            msg_ps->vb_ptr->ident_len = 0;

            vb->value_type = msg_ps->ext_object_def.asn_type;
            LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
            vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
            if (vb->value_len > 0) {
                LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
                vb->value = memp_malloc(MEMP_SNMP_VALUE);
                if (vb->value != NULL) {
                    en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
                    snmp_varbind_tail_add(&msg_ps->outvb, vb);
                    /* search again (if vb_idx < msg_ps->invb.count) */
                    msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                    msg_ps->vb_idx += 1;
                } else {
                    en->get_value_pc(request_id, &msg_ps->ext_object_def);
                    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
                    msg_ps->vb_ptr->ident = vb->ident;
                    msg_ps->vb_ptr->ident_len = vb->ident_len;
                    memp_free(MEMP_SNMP_VARBIND, vb);
                    snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
                }
            } else {
                /* vb->value_len == 0, empty value (e.g. empty string) */
                en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
                vb->value = NULL;
                snmp_varbind_tail_add(&msg_ps->outvb, vb);
                /* search again (if vb_idx < msg_ps->invb.count) */
                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                msg_ps->vb_idx += 1;
            }
        } else {
            en->get_value_pc(request_id, &msg_ps->ext_object_def);
            LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
            snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
        }
    }

    while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
            (msg_ps->vb_idx < msg_ps->invb.count)) {
        struct mib_node *mn;
        struct snmp_name_ptr np;

        if (msg_ps->vb_idx == 0) {
            msg_ps->vb_ptr = msg_ps->invb.head;
        } else {
            msg_ps->vb_ptr = msg_ps->vb_ptr->next;
        }
        /** test object identifier for .iso.org.dod.internet prefix */
        if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident)) {
            mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
                                  msg_ps->vb_ptr->ident + 4, &np);
            if (mn != NULL) {
                if (mn->node_type == MIB_NODE_EX) {
                    /* external object */
                    struct mib_external_node *en = (struct mib_external_node*)mn;

                    msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
                    /* save en && args in msg_ps!! */
                    msg_ps->ext_mib_node = en;
                    msg_ps->ext_name_ptr = np;

                    en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
                } else {
                    /* internal object */
                    struct obj_def object_def;

                    msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
                    mn->get_object_def(np.ident_len, np.ident, &object_def);
                    if ((object_def.instance != MIB_OBJECT_NONE) &&
                            (object_def.access & MIB_ACCESS_READ)) {
                        mn = mn;
                    } else {
                        /* search failed, object id points to unknown object (nosuchname) */
                        mn =  NULL;
                    }
                    if (mn != NULL) {
                        struct snmp_varbind *vb;

                        msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
                        /* allocate output varbind */
                        vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
                        if (vb != NULL) {
                            vb->next = NULL;
                            vb->prev = NULL;

                            /* move name from invb to outvb */
                            vb->ident = msg_ps->vb_ptr->ident;
                            vb->ident_len = msg_ps->vb_ptr->ident_len;
                            /* ensure this memory is refereced once only */
                            msg_ps->vb_ptr->ident = NULL;
                            msg_ps->vb_ptr->ident_len = 0;

                            vb->value_type = object_def.asn_type;
                            LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
                            vb->value_len = (u8_t)object_def.v_len;
                            if (vb->value_len > 0) {
                                LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
                                            vb->value_len <= SNMP_MAX_VALUE_SIZE);
                                vb->value = memp_malloc(MEMP_SNMP_VALUE);
                                if (vb->value != NULL) {
                                    mn->get_value(&object_def, vb->value_len, vb->value);
                                    snmp_varbind_tail_add(&msg_ps->outvb, vb);
                                    msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                                    msg_ps->vb_idx += 1;
                                } else {
                                    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
                                    msg_ps->vb_ptr->ident = vb->ident;
                                    msg_ps->vb_ptr->ident_len = vb->ident_len;
                                    vb->ident = NULL;
                                    vb->ident_len = 0;
                                    memp_free(MEMP_SNMP_VARBIND, vb);
                                    snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
                                }
                            } else {
                                /* vb->value_len == 0, empty value (e.g. empty string) */
                                vb->value = NULL;
                                snmp_varbind_tail_add(&msg_ps->outvb, vb);
                                msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                                msg_ps->vb_idx += 1;
                            }
                        } else {
                            LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
                            snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
                        }
                    }
                }
            }
        } else {
            mn = NULL;
        }
        if (mn == NULL) {
            /* mn == NULL, noSuchName */
            snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
        }
    }
    if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
            (msg_ps->vb_idx == msg_ps->invb.count)) {
        snmp_ok_response(msg_ps);
    }
}
Exemplo n.º 3
0
/**
 * Service an internal or external event for SNMP GETNEXT.
 *
 * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
 * @param msg_ps points to the assosicated message process state
 */
static void
snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
{
    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));

    if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) {
        struct mib_external_node *en;

        /* get_object_def() answer*/
        en = msg_ps->ext_mib_node;

        /* translate answer into a known lifeform */
        en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
        if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) {
            msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
            en->get_value_q(request_id, &msg_ps->ext_object_def);
        } else {
            en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
            /* search failed, object id points to unknown object (nosuchname) */
            snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
        }
    } else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) {
        struct mib_external_node *en;
        struct snmp_varbind *vb;

        /* get_value() answer */
        en = msg_ps->ext_mib_node;

        LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
        vb = snmp_varbind_alloc(&msg_ps->ext_oid,
                                msg_ps->ext_object_def.asn_type,
                                (u8_t)msg_ps->ext_object_def.v_len);
        if (vb != NULL) {
            en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
            snmp_varbind_tail_add(&msg_ps->outvb, vb);
            msg_ps->state = SNMP_MSG_SEARCH_OBJ;
            msg_ps->vb_idx += 1;
        } else {
            en->get_value_pc(request_id, &msg_ps->ext_object_def);
            LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
            snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
        }
    }

    while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
            (msg_ps->vb_idx < msg_ps->invb.count)) {
        struct mib_node *mn;
        struct snmp_obj_id oid;

        if (msg_ps->vb_idx == 0) {
            msg_ps->vb_ptr = msg_ps->invb.head;
        } else {
            msg_ps->vb_ptr = msg_ps->vb_ptr->next;
        }
        if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) {
            if (msg_ps->vb_ptr->ident_len > 3) {
                /* can offset ident_len and ident */
                mn = snmp_expand_tree((struct mib_node*)&internet,
                                      msg_ps->vb_ptr->ident_len - 4,
                                      msg_ps->vb_ptr->ident + 4, &oid);
            } else {
                /* can't offset ident_len -4, ident + 4 */
                mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
            }
        } else {
            mn = NULL;
        }
        if (mn != NULL) {
            if (mn->node_type == MIB_NODE_EX) {
                /* external object */
                struct mib_external_node *en = (struct mib_external_node*)mn;

                msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
                /* save en && args in msg_ps!! */
                msg_ps->ext_mib_node = en;
                msg_ps->ext_oid = oid;

                en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
            } else {
                /* internal object */
                struct obj_def object_def;
                struct snmp_varbind *vb;

                msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
                mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);

                LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
                vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
                if (vb != NULL) {
                    msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
                    mn->get_value(&object_def, object_def.v_len, vb->value);
                    snmp_varbind_tail_add(&msg_ps->outvb, vb);
                    msg_ps->state = SNMP_MSG_SEARCH_OBJ;
                    msg_ps->vb_idx += 1;
                } else {
                    LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
                    snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
                }
            }
        }
        if (mn == NULL) {
            /* mn == NULL, noSuchName */
            snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
        }
    }
    if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
            (msg_ps->vb_idx == msg_ps->invb.count)) {
        snmp_ok_response(msg_ps);
    }
}
Exemplo n.º 4
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;
}