CVE-2020-12141 (C, published 2021-10-19T16:15Z)
Description: value: An out-of-bounds read in the SNMP stack in Contiki-NG 4.4 and earlier allows an attacker to cause a denial of service and potentially disclose information via crafted SNMP packets to snmp_ber_decode_string_len_buffer in os/net/app-layer/snmp/snmp-ber.c.
Severity: MEDIUM (CVSS v3 base score: 9.1)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/contiki-ng/contiki-ng
Files changed:
examples/snmp-server/project-conf.h
examples/snmp-server/resources/snmp-SNMP-MIB-2-System.c
examples/snmp-server/snmp-server.c
os/net/app-layer/snmp/snmp-api.c
os/net/app-layer/snmp/snmp-api.h
os/net/app-layer/snmp/snmp-ber.c
os/net/app-layer/snmp/snmp-ber.h
os/net/app-layer/snmp/snmp-conf.h
os/net/app-layer/snmp/snmp-engine.c
os/net/app-layer/snmp/snmp-engine.h
os/net/app-layer/snmp/snmp-message.c
os/net/app-layer/snmp/snmp-message.h
os/net/app-layer/snmp/snmp-mib.c
os/net/app-layer/snmp/snmp-mib.h
os/net/app-layer/snmp/snmp.c
os/net/app-layer/snmp/snmp.h
tests/08-native-runs/10-snmp-server.sh
Commit message: Refactored SNMP engine after vulnerabilities


Vulnerable code:
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
sysDescr_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysDescr_handler(snmp_varbind_t *varbind, uint32_t *oid)
sysObjectID_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysObjectID_handler(snmp_varbind_t *varbind, uint32_t *oid)
  snmp_api_set_oid(varbind, oid, sysObjectID_oid);
sysUpTime_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysUpTime_handler(snmp_varbind_t *varbind, uint32_t *oid)
sysContact_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysContact_handler(snmp_varbind_t *varbind, uint32_t *oid)
sysName_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysName_handler(snmp_varbind_t *varbind, uint32_t *oid)
sysLocation_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysLocation_handler(snmp_varbind_t *varbind, uint32_t *oid)
sysServices_handler(snmp_varbind_t *varbind, uint32_t *oid);
sysServices_handler(snmp_varbind_t *varbind, uint32_t *oid)
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#include "snmp-oid.h"
static void
snmp_api_replace_oid(snmp_varbind_t *varbind, uint32_t *oid)
{
  uint8_t i;

  i = 0;
  while(oid[i] != ((uint32_t)-1)) {
    varbind->oid[i] = oid[i];
    i++;
  }
  varbind->oid[i] = ((uint32_t)-1);
}
snmp_api_set_string(snmp_varbind_t *varbind, uint32_t *oid, char *string)

  snmp_api_replace_oid(varbind, oid);
snmp_api_set_time_ticks(snmp_varbind_t *varbind, uint32_t *oid, uint32_t integer)

  snmp_api_replace_oid(varbind, oid);
  varbind->value_type = SNMP_DATA_TYPE_TIME_TICKS;
snmp_api_set_oid(snmp_varbind_t *varbind, uint32_t *oid, uint32_t *ret_oid)

  snmp_api_replace_oid(varbind, oid);
  varbind->value_type = BER_DATA_TYPE_OID;
  varbind->value.oid = ret_oid;
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      The public API for the Contiki-NG SNMP implementation
 * \defgroup SNMPAPI This is the SNMP Public API
 * The function outside this header can be changed without notice
typedef void (*snmp_mib_resource_handler_t)(snmp_varbind_t *varbind, uint32_t *oid);
  static uint32_t name[] = { __VA_ARGS__, -1 };
  uint32_t name##_oid[] = { __VA_ARGS__, -1 }; \
  snmp_mib_resource_t name = { NULL, name##_oid, handler };
snmp_api_set_string(snmp_varbind_t *varbind, uint32_t *oid, char *string);
snmp_api_set_time_ticks(snmp_varbind_t *varbind, uint32_t *oid, uint32_t integer);
snmp_api_set_oid(snmp_varbind_t *varbind, uint32_t *oid, uint32_t *ret_oid);
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
unsigned char *
snmp_ber_encode_type(unsigned char *out, uint32_t *out_len, uint8_t type)
  *out-- = type;
  (*out_len)++;
  return out;
}
/*---------------------------------------------------------------------------*/
unsigned char *
snmp_ber_encode_length(unsigned char *out, uint32_t *out_len, uint8_t length)
{
  *out-- = length;
  (*out_len)++;
  return out;
}
/*---------------------------------------------------------------------------*/
unsigned char *
snmp_ber_encode_integer(unsigned char *out, uint32_t *out_len, uint32_t number)
{
  uint32_t original_out_len;
  original_out_len = *out_len;
    (*out_len)++;
    *out-- = (uint8_t)(number & 0xFF);
  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, BER_DATA_TYPE_INTEGER);
  return out;
unsigned char *
snmp_ber_encode_unsigned_integer(unsigned char *out, uint32_t *out_len, uint8_t type, uint32_t number)
  uint32_t original_out_len;
  original_out_len = *out_len;
  do {
    (*out_len)++;
    *out-- = (uint8_t)(number & 0xFF);
    number >>= 8;
  } while(number);
  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, type);
  return out;
unsigned char *
snmp_ber_encode_string_len(unsigned char *out, uint32_t *out_len, const char *str, uint32_t length)
    (*out_len)++;
    *out-- = (uint8_t)*str--;
  out = snmp_ber_encode_length(out, out_len, length);
  out = snmp_ber_encode_type(out, out_len, BER_DATA_TYPE_OCTET_STRING);
  return out;
}
/*---------------------------------------------------------------------------*/
unsigned char *
snmp_ber_encode_null(unsigned char *out, uint32_t *out_len, uint8_t type)
{
  (*out_len)++;
  *out-- = 0x00;
  out = snmp_ber_encode_type(out, out_len, type);
  return out;
unsigned char *
snmp_ber_decode_type(unsigned char *buff, uint32_t *buff_len, uint8_t *type)
  if(*buff_len == 0) {
    return NULL;
  *type = *buff++;
  (*buff_len)--;
  return buff;
unsigned char *
snmp_ber_decode_length(unsigned char *buff, uint32_t *buff_len, uint8_t *length)
  if(*buff_len == 0) {
    return NULL;
  *length = *buff++;
  (*buff_len)--;
  return buff;
unsigned char *
snmp_ber_decode_integer(unsigned char *buf, uint32_t *buff_len, uint32_t *num)
  buf = snmp_ber_decode_type(buf, buff_len, &type);
  if(buf == NULL || type != BER_DATA_TYPE_INTEGER) {
    return NULL;
  buf = snmp_ber_decode_length(buf, buff_len, &len);
  if(buf == NULL || len > 4) {
    return NULL;
  if(*buff_len < len) {
    return NULL;
  *num = (uint32_t)(*buf++ & 0xFF);
  (*buff_len)--;
    *num |= (uint8_t)(*buf++ & 0xFF);
    (*buff_len)--;
  return buf;
unsigned char *
snmp_ber_decode_unsigned_integer(unsigned char *buf, uint32_t *buff_len, uint8_t expected_type, uint32_t *num)
  uint8_t i, len, type;

  buf = snmp_ber_decode_type(buf, buff_len, &type);

  if(buf == NULL || type != expected_type) {
    /*
     * Sanity check
     * Invalid type in buffer
     */
    return NULL;
  buf = snmp_ber_decode_length(buf, buff_len, &len);

  if(buf == NULL || len > 4) {
    /*
     * Sanity check
     * It will not fit in the uint32_t
     */
    return NULL;
  }
  if(*buff_len < len) {
    return NULL;
  *num = (uint32_t)(*buf++ & 0xFF);
  (*buff_len)--;
  for(i = 1; i < len; ++i) {
    *num <<= 8;
    *num |= (uint8_t)(*buf++ & 0xFF);
    (*buff_len)--;
  }
  return buf;
unsigned char *
snmp_ber_decode_string_len_buffer(unsigned char *buf, uint32_t *buff_len, const char **str, uint32_t *length)
  buf = snmp_ber_decode_type(buf, buff_len, &type);
  if(buf == NULL || type != BER_DATA_TYPE_OCTET_STRING) {
    return NULL;
  if((*buf & 0x80) == 0) {
    *length = (uint32_t)*buf++;
    (*buff_len)--;
    length_bytes = (uint8_t)(*buf++ & 0x7F);
    (*buff_len)--;
      return NULL;
    *length = (uint32_t)*buf++;
    (*buff_len)--;
      *length |= *buf++;
      (*buff_len)--;
  *str = (const char *)buf;
  *buff_len -= *length;
  return buf + *length;
unsigned char *
snmp_ber_decode_null(unsigned char *buf, uint32_t *buff_len)
  buf++;
  (*buff_len)--;
  buf++;
  (*buff_len)--;
  return buf;
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#define BER_DATA_TYPE_INTEGER           0x02
#define BER_DATA_TYPE_OCTET_STRING      0x04
#define BER_DATA_TYPE_NULL              0x05
#define BER_DATA_TYPE_OID               0x06
#define BER_DATA_TYPE_SEQUENCE          0x30
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_type(unsigned char *out, uint32_t *out_len, uint8_t type);
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_length(unsigned char *out, uint32_t *out_len, uint8_t length);
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_integer(unsigned char *out, uint32_t *out_len, uint32_t integer);
 * @brief Encodes an unsigned integer
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @param type A type that represents an unsigned integer
 * @param number A number
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_unsigned_integer(unsigned char *out, uint32_t *out_len, uint8_t type, uint32_t number);
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_string_len(unsigned char *out, uint32_t *out_len, const char *str, uint32_t length);
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @return NULL if error or the next entry in the buffer
unsigned char *
snmp_ber_encode_null(unsigned char *out, uint32_t *out_len, uint8_t type);
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_type(unsigned char *buff, uint32_t *buff_len, uint8_t *type);
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_length(unsigned char *buff, uint32_t *buff_len, uint8_t *length);
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_integer(unsigned char *buff, uint32_t *buff_len, uint32_t *integer);
 * @brief Decodes an unsigned number
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @param expected_type The expected type that represents an unsingned integer
 * @param number A pointer to the number
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_unsigned_integer(unsigned char *buff, uint32_t *buff_len, uint8_t expected_type, uint32_t *number);
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_string_len_buffer(unsigned char *buff, uint32_t *buff_len, const char **str, uint32_t *length);
 * @param buff A pointer to the beginning of the buffer
 * @param buff_len A pointer to the buffer length
 * @return NULL if error or the first entry after the oid in the buffer
unsigned char *
snmp_ber_decode_null(unsigned char *buff, uint32_t *buff_len);
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Configurable Macros
 * \defgroup SNMPConfs SNMP Configurable Defines
/**
 * \brief Configurable maximum size of the packet in bytes
 */
#define SNMP_MAX_PACKET_SIZE SNMP_CONF_MAX_PACKET_SIZE
#else
/**
 * \brief Default maximum size of the packet in bytes
 */
#define SNMP_MAX_PACKET_SIZE 512
#endif
/*@}*/
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#include "snmp-oid.h"
int
snmp_engine_get(snmp_header_t *header, snmp_varbind_t *varbinds, uint32_t varbinds_length)
  uint32_t i;
  for(i = 0; i < varbinds_length; i++) {
    resource = snmp_mib_find(varbinds[i].oid);
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = i + 1;
        (&varbinds[i])->value_type = SNMP_DATA_TYPE_NO_SUCH_INSTANCE;
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = 0;
      resource->handler(&varbinds[i], resource->oid);
int
snmp_engine_get_next(snmp_header_t *header, snmp_varbind_t *varbinds, uint32_t varbinds_length)
  uint32_t i;
  for(i = 0; i < varbinds_length; i++) {
    resource = snmp_mib_find_next(varbinds[i].oid);
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = i + 1;
        (&varbinds[i])->value_type = SNMP_DATA_TYPE_END_OF_MIB_VIEW;
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = 0;
      resource->handler(&varbinds[i], resource->oid);
int
snmp_engine_get_bulk(snmp_header_t *header, snmp_varbind_t *varbinds, uint32_t *varbinds_length)
  uint32_t i, j, original_varbinds_length;
  uint32_t oid[SNMP_MAX_NR_VALUES][SNMP_MSG_OID_MAX_LEN];
  original_varbinds_length = *varbinds_length;
  for(i = 0; i < original_varbinds_length; i++) {
    snmp_oid_copy(oid[i], varbinds[i].oid);
  *varbinds_length = 0;
    if(i >= header->error_status_non_repeaters.non_repeaters) {
    resource = snmp_mib_find_next(oid[i]);
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = i + 1;
        (&varbinds[i])->value_type = SNMP_DATA_TYPE_END_OF_MIB_VIEW;
        header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index_max_repetitions.error_index = 0;
      if(*varbinds_length < SNMP_MAX_NR_VALUES) {
        resource->handler(&varbinds[*varbinds_length], resource->oid);
        (*varbinds_length)++;
  for(i = 0; i < header->error_index_max_repetitions.max_repetitions; i++) {
    for(j = header->error_status_non_repeaters.non_repeaters; j < original_varbinds_length; j++) {
      resource = snmp_mib_find_next(oid[j]);
          header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
          header->error_index_max_repetitions.error_index = *varbinds_length + 1;
          if(*varbinds_length < SNMP_MAX_NR_VALUES) {
            (&varbinds[*varbinds_length])->value_type = SNMP_DATA_TYPE_END_OF_MIB_VIEW;
            snmp_oid_copy((&varbinds[*varbinds_length])->oid, oid[j]);
            (*varbinds_length)++;
          header->error_status_non_repeaters.error_status = SNMP_STATUS_NO_SUCH_NAME;
          header->error_index_max_repetitions.error_index = 0;
        if(*varbinds_length < SNMP_MAX_NR_VALUES) {
          resource->handler(&varbinds[*varbinds_length], resource->oid);
          (*varbinds_length)++;
          snmp_oid_copy(oid[j], resource->oid);
unsigned char *
snmp_engine(unsigned char *buff, uint32_t buff_len, unsigned char *out, uint32_t *out_len)
  static snmp_header_t header;
  static snmp_varbind_t varbinds[SNMP_MAX_NR_VALUES];
  static uint32_t varbind_length = SNMP_MAX_NR_VALUES;
  buff = snmp_message_decode(buff, buff_len, &header, varbinds, &varbind_length);
  if(buff == NULL) {
    return NULL;
      return NULL;
  case SNMP_DATA_TYPE_PDU_GET_REQUEST:
    if(snmp_engine_get(&header, varbinds, varbind_length) == -1) {
      return NULL;
  case SNMP_DATA_TYPE_PDU_GET_NEXT_REQUEST:
    if(snmp_engine_get_next(&header, varbinds, varbind_length) == -1) {
      return NULL;
  case SNMP_DATA_TYPE_PDU_GET_BULK:
    if(snmp_engine_get_bulk(&header, varbinds, &varbind_length) == -1) {
      return NULL;
    return NULL;
  header.pdu_type = SNMP_DATA_TYPE_PDU_GET_RESPONSE;
  out = snmp_message_encode(out, out_len, &header, varbinds, varbind_length);
  return ++out;
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
 * @brief Process the SNMP packet and prepares the response
 * @param buff A pointer to the beginning of the packet buffer
 * @param buff_len The packet length
 * @param out A pointer to the end of the response buffer
 * @param out_len A pointer to the length of the response buffer
 * @return NULL in case of fail or the first element in the response buffer
unsigned char *
snmp_engine(unsigned char *buff, uint32_t buff_len, unsigned char *out, uint32_t *out_len);
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#include "snmp-oid.h"
unsigned char *
snmp_message_encode(unsigned char *out, uint32_t *out_len, snmp_header_t *header,
                    snmp_varbind_t *varbinds, uint32_t varbind_num)
  snmp_varbind_t *varbind;
  uint32_t original_out_len, last_out_len;
  original_out_len = *out_len;
  for(i = varbind_num - 1; i >= 0; i--) {
    varbind = &varbinds[i];
    last_out_len = *out_len;
    switch(varbind->value_type) {
      out = snmp_ber_encode_integer(out, out_len, varbind->value.integer);
    case SNMP_DATA_TYPE_TIME_TICKS:
      out = snmp_ber_encode_unsigned_integer(out, out_len, varbind->value_type, varbind->value.integer);
      out = snmp_ber_encode_string_len(out, out_len, varbind->value.string.string, varbind->value.string.length);
    case BER_DATA_TYPE_OID:
      out = snmp_oid_encode_oid(out, out_len, varbind->value.oid);
    case SNMP_DATA_TYPE_NO_SUCH_INSTANCE:
    case SNMP_DATA_TYPE_END_OF_MIB_VIEW:
      out = snmp_ber_encode_null(out, out_len, varbind->value_type);
      return NULL;
    out = snmp_oid_encode_oid(out, out_len, varbind->oid);
    out = snmp_ber_encode_length(out, out_len, ((*out_len - last_out_len) & 0xFF));
    out = snmp_ber_encode_type(out, out_len, BER_DATA_TYPE_SEQUENCE);
  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, BER_DATA_TYPE_SEQUENCE);
  if(header->pdu_type == SNMP_DATA_TYPE_PDU_GET_BULK) {
    out = snmp_ber_encode_integer(out, out_len, header->error_index_max_repetitions.max_repetitions);
    out = snmp_ber_encode_integer(out, out_len, header->error_status_non_repeaters.non_repeaters);
  } else {
    out = snmp_ber_encode_integer(out, out_len, header->error_index_max_repetitions.error_index);
    out = snmp_ber_encode_integer(out, out_len, header->error_status_non_repeaters.error_status);
  out = snmp_ber_encode_integer(out, out_len, header->request_id);
  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, header->pdu_type);
  out = snmp_ber_encode_string_len(out, out_len, header->community.community, header->community.length);
  out = snmp_ber_encode_integer(out, out_len, header->version);
  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, BER_DATA_TYPE_SEQUENCE);
  return out;
uint8_t *
snmp_message_decode(uint8_t *buf, uint32_t buf_len, snmp_header_t *header,
                    snmp_varbind_t *varbinds, uint32_t *varbind_num)
  uint8_t type, len;
  uint32_t i, oid_len = SNMP_MSG_OID_MAX_LEN;
  buf = snmp_ber_decode_type(buf, &buf_len, &type);
  if(buf == NULL) {
    return NULL;
    return NULL;
  buf = snmp_ber_decode_length(buf, &buf_len, &len);
  if(buf == NULL) {
    return NULL;
  buf = snmp_ber_decode_integer(buf, &buf_len, &header->version);
  if(buf == NULL) {
    return NULL;
  buf = snmp_ber_decode_string_len_buffer(buf, &buf_len, &header->community.community, &header->community.length);
  if(buf == NULL) {
    LOG_DBG("Could not decode community\n");
    return NULL;
  if(header->version != SNMP_VERSION_1 &&
     header->version != SNMP_VERSION_2C) {
    LOG_DBG("Invalid version\n");
    return NULL;
  buf = snmp_ber_decode_type(buf, &buf_len, &type);
  if(buf == NULL) {
    LOG_DBG("Could not decode type\n");
    return NULL;
  header->pdu_type = type;
  if(header->pdu_type != SNMP_DATA_TYPE_PDU_GET_REQUEST &&
     header->pdu_type != SNMP_DATA_TYPE_PDU_GET_NEXT_REQUEST &&
     header->pdu_type != SNMP_DATA_TYPE_PDU_GET_RESPONSE &&
     header->pdu_type != SNMP_DATA_TYPE_PDU_SET_REQUEST &&
     header->pdu_type != SNMP_DATA_TYPE_PDU_GET_BULK) {
    LOG_DBG("Invalid pdu type\n");
    return NULL;
  buf = snmp_ber_decode_length(buf, &buf_len, &len);
  if(buf == NULL) {
    return NULL;
  buf = snmp_ber_decode_integer(buf, &buf_len, &header->request_id);
  if(buf == NULL) {
    return NULL;
  if(header->pdu_type == SNMP_DATA_TYPE_PDU_GET_BULK) {
    buf = snmp_ber_decode_integer(buf, &buf_len, &header->error_status_non_repeaters.non_repeaters);
    if(buf == NULL) {
      LOG_DBG("Could not decode error status\n");
      return NULL;
    buf = snmp_ber_decode_integer(buf, &buf_len, &header->error_index_max_repetitions.max_repetitions);
    if(buf == NULL) {
      LOG_DBG("Could not decode error index\n");
      return NULL;
  } else {
    buf = snmp_ber_decode_integer(buf, &buf_len, &header->error_status_non_repeaters.error_status);
    if(buf == NULL) {
      return NULL;
    buf = snmp_ber_decode_integer(buf, &buf_len, &header->error_index_max_repetitions.error_index);
    if(buf == NULL) {
      return NULL;
  buf = snmp_ber_decode_type(buf, &buf_len, &type);
  if(buf == NULL) {
    return NULL;
    return NULL;
  buf = snmp_ber_decode_length(buf, &buf_len, &len);
  if(buf == NULL) {
    return NULL;
  for(i = 0; buf_len > 0; ++i) {
    if(i >= *varbind_num) {
      return NULL;
    buf = snmp_ber_decode_type(buf, &buf_len, &type);
    if(buf == NULL) {
      return NULL;
      return NULL;
    buf = snmp_ber_decode_length(buf, &buf_len, &len);
    if(buf == NULL) {
      return NULL;
    buf = snmp_oid_decode_oid(buf, &buf_len, varbinds[i].oid, &oid_len);
    if(buf == NULL) {
      return NULL;
    varbinds[i].value_type = *buf;
      buf = snmp_ber_decode_integer(buf, &buf_len, &varbinds[i].value.integer);
    case SNMP_DATA_TYPE_TIME_TICKS:
      buf = snmp_ber_decode_unsigned_integer(buf, &buf_len, varbinds[i].value_type, &varbinds[i].value.integer);
      buf = snmp_ber_decode_string_len_buffer(buf, &buf_len, &varbinds[i].value.string.string, &varbinds[i].value.string.length);
      buf = snmp_ber_decode_null(buf, &buf_len);
      return NULL;
    }

    if(buf == NULL) {
      LOG_DBG("Could varbind type\n");
      return NULL;
  *varbind_num = i;

  return buf;
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#include "snmp.h"
#define SNMP_DATA_TYPE_TIME_TICKS               0x43
#define SNMP_DATA_TYPE_NO_SUCH_INSTANCE         0x81
#define SNMP_DATA_TYPE_END_OF_MIB_VIEW          0x82
#define SNMP_DATA_TYPE_PDU_GET_REQUEST          0xA0
#define SNMP_DATA_TYPE_PDU_GET_NEXT_REQUEST     0xA1
#define SNMP_DATA_TYPE_PDU_GET_RESPONSE         0xA2
#define SNMP_DATA_TYPE_PDU_SET_REQUEST          0xA3
#define SNMP_DATA_TYPE_PDU_TRAP                 0xA4
#define SNMP_DATA_TYPE_PDU_GET_BULK             0xA5
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @param varbinds_length The number of varbinds
unsigned char *
snmp_message_encode(unsigned char *out, uint32_t *out_len, snmp_header_t *header,
                    snmp_varbind_t *varbinds, uint32_t varbinds_length);
 * @param buf A pointer to the beginning of the buffer
 * @param buf_len A pointer to the buffer length
 * @param varbinds_length A pointer to the number of varbinds
uint8_t *
snmp_message_decode(uint8_t *buf, uint32_t buf_len, snmp_header_t *header,
                    snmp_varbind_t *varbinds, uint32_t *varbinds_length);
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
#include "snmp-oid.h"
snmp_mib_find(uint32_t *oid)
    if(!snmp_oid_cmp_oid(oid, resource->oid)) {
snmp_mib_find_next(uint32_t *oid)
    if(snmp_oid_cmp_oid(resource->oid, oid) > 0) {
    if(snmp_oid_cmp_oid(resource->oid, new_resource->oid) > 0) {
#if LOG_LEVEL == LOG_LEVEL_DBG
  /*
   * We print the entire resource table
   */
  LOG_DBG("Table after insert.\n");
  for(resource = list_head(snmp_mib);
      resource; resource = resource->next) {

    snmp_oid_print(resource->oid);
#endif /* LOG_LEVEL == LOG_LEVEL_DBG  */
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
typedef void (*snmp_mib_resource_handler_t)(snmp_varbind_t *varbind, uint32_t *oid);
   * @brief A array that represents the OID
   *
   * @remarks This array is "null" terminated. In this case the -1 is used.
  uint32_t *oid;
snmp_mib_find(uint32_t *oid);
snmp_mib_find_next(uint32_t *oid);
/*
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*---------------------------------------------------------------------------*/

/**
 * \file
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
 * \author
 *      Yago Fontoura do Rosario <yago.rosario@hotmail.com.br
 */

#include "contiki.h"

#include "snmp-oid.h"
#include "snmp-ber.h"

#define LOG_MODULE "SNMP [oid]"
#define LOG_LEVEL LOG_LEVEL_SNMP

/*---------------------------------------------------------------------------*/
int
snmp_oid_cmp_oid(uint32_t *oid1, uint32_t *oid2)
{
  uint8_t i;

  i = 0;
  while(oid1[i] != ((uint32_t)-1) &&
        oid2[i] != ((uint32_t)-1)) {
    if(oid1[i] != oid2[i]) {
      if(oid1[i] < oid2[i]) {
        return -1;
      }
      return 1;
    }
    i++;
  }

  if(oid1[i] == ((uint32_t)-1) &&
     oid2[i] != ((uint32_t)-1)) {
    return -1;
  }

  if(oid1[i] != ((uint32_t)-1) &&
     oid2[i] == ((uint32_t)-1)) {
    return 1;
  }

  return 0;
}
/*---------------------------------------------------------------------------*/
unsigned char *
snmp_oid_encode_oid(unsigned char *out, uint32_t *out_len, uint32_t *oid)
{
  uint32_t original_out_len;
  uint32_t *oid_start = oid;
  uint32_t num;

  original_out_len = *out_len;
  while(*oid != ((uint32_t)-1)) {
    ++oid;
  }
  --oid;

  while(oid != oid_start) {
    num = *oid;
    (*out_len)++;
    *out-- = (uint8_t)(num & 0x7F);
    num >>= 7;

    while(num) {
      (*out_len)++;
      *out-- = (uint8_t)((num & 0x7F) | 0x80);
      num >>= 7;
    }
    --oid;
  }

  num = *(out + 1) + 40 * *oid;
  (*out_len)--;
  out++;
  (*out_len)++;
  *out-- = (uint8_t)(num & 0x7F);
  num >>= 7;

  while(num) {
    (*out_len)++;
    *out-- = (uint8_t)((num & 0x7F) | 0x80);
    num >>= 7;
  }

  out = snmp_ber_encode_length(out, out_len, ((*out_len - original_out_len) & 0xFF));
  out = snmp_ber_encode_type(out, out_len, SNMP_DATA_TYPE_OBJECT);

  return out;
}
/*---------------------------------------------------------------------------*/
uint8_t *
snmp_oid_decode_oid(uint8_t *buf, uint32_t *buff_len, uint32_t *oid, uint32_t *oid_len)
{
  uint32_t *start;
  uint8_t *buf_end, type;
  uint8_t len;
  div_t first;

  start = oid;

  buf = snmp_ber_decode_type(buf, buff_len, &type);
  if(buf == NULL) {
    return NULL;
  }

  if(type != SNMP_DATA_TYPE_OBJECT) {
    return NULL;
  }

  buf = snmp_ber_decode_length(buf, buff_len, &len);
  if(buf == NULL) {
    return NULL;
  }

  buf_end = buf + len;

  (*buff_len)--;
  first = div(*buf++, 40);
  *oid++ = (uint32_t)first.quot;
  *oid++ = (uint32_t)first.rem;

  while(buf != buf_end) {
    --(*oid_len);
    if(*oid_len == 0) {
      return NULL;
    }

    int i;

    *oid = (uint32_t)(*buf & 0x7F);
    for(i = 0; i < 4; i++) {
      (*buff_len)--;
      if((*buf++ & 0x80) == 0) {
        break;
      }

      *oid <<= 7;
      *oid |= (*buf & 0x7F);
    }

    ++oid;
  }

  *oid++ = ((uint32_t)-1);
  *oid_len = (uint32_t)(oid - start);

  return buf;
}
/*---------------------------------------------------------------------------*/
void
snmp_oid_copy(uint32_t *dst, uint32_t *src)
{
  uint8_t i;

  i = 0;
  while(src[i] != ((uint32_t)-1)) {
    dst[i] = src[i];
    i++;
  }
  /*
   * Copy the "null" terminator
   */
  dst[i] = src[i];
}
/*---------------------------------------------------------------------------*/
#if LOG_LEVEL == LOG_LEVEL_DBG
void
snmp_oid_print(uint32_t *oid)
{
  uint8_t i;

  i = 0;
  LOG_DBG("{");
  while(oid[i] != ((uint32_t)-1)) {
    LOG_DBG_("%lu", (unsigned long)oid[i]);
    i++;
    if(oid[i] != ((uint32_t)-1)) {
      LOG_DBG_(".");
    }
  }
  LOG_DBG_("}\n");
}
#endif /* LOG_LEVEL == LOG_LEVEL_DBG  */
/*
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*---------------------------------------------------------------------------*/

/**
 * \file
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
 * \author
 *      Yago Fontoura do Rosario <yago.rosario@hotmail.com.br
 */

/**
 * \addtogroup snmp
 * @{
 */

#ifndef SNMP_OID_H_
#define SNMP_OID_H_

#include "snmp.h"

#define SNMP_DATA_TYPE_OBJECT                   0x06

/**
 * @brief Compares to oids
 *
 * @param oid1 First Oid
 * @param oid2 Second Oid
 *
 * @return < 0 if oid1 < oid2, > 0 if oid1 > oid2 and 0 if they are equal
 */
int
snmp_oid_cmp_oid(uint32_t *oid1, uint32_t *oid2);

/**
 * @brief Encodes a Oid
 *
 * @param out A pointer to the end of the buffer
 * @param out_len A pointer to the buffer length
 * @param oid The Oid
 *
 * @return NULL if error or the next entry in the buffer
 */
unsigned char *
snmp_oid_encode_oid(unsigned char *out, uint32_t *out_len, uint32_t *oid);

/**
 * @brief Decodes a Oid
 *
 * @param buf A pointer to the beginning of the buffer
 * @param buf_len A pointer to the buffer length
 * @param oid A pointer to the oid array
 * @param oid_len A pointer to the oid length
 *
 * @return NULL if error or the first entry after the oid in the buffer
 */
unsigned char *
snmp_oid_decode_oid(unsigned char *buf, uint32_t *buf_len, uint32_t *oid, uint32_t *oid_len);

/**
 * @brief Copies a Oid
 *
 * @param dst A pointer to the destination array
 * @param src A pointer to the source array
 */
void
snmp_oid_copy(uint32_t *dst, uint32_t *src);

#if LOG_LEVEL == LOG_LEVEL_DBG
/**
 * @brief Prints a oid
 *
 * @param oid A oid
 */
void
snmp_oid_print(uint32_t *oid);
#endif /* LOG_LEVEL == LOG_LEVEL_DBG */

#endif /* SNMP_OID_H_ */
/** @} */
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
static struct uip_udp_conn *snmp_udp_conn = NULL;

/*---------------------------------------------------------------------------*/
static void
snmp_process_data(void)
{
  static unsigned char packet[SNMP_MAX_PACKET_SIZE];
  unsigned char *packet_end;
  static uint32_t packet_len;

  packet_end = packet + sizeof(packet) - 1;
  packet_len = 0;

  LOG_DBG("receiving UDP datagram from [");
  LOG_DBG_6ADDR(&UIP_IP_BUF->srcipaddr);
  LOG_DBG_("]:%u", uip_ntohs(UIP_UDP_BUF->srcport));
  LOG_DBG_(" Length: %u\n", uip_datalen());

  /*
   * Handle the request
   */
  if((packet_end = snmp_engine(uip_appdata, uip_datalen(), packet_end, &packet_len)) == NULL) {
    LOG_DBG("Error while handling the request\n");
  } else {
    LOG_DBG("Sending response\n");
    /*
     * Send the response
     */
    uip_udp_packet_sendto(snmp_udp_conn, packet_end, packet_len, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport);
  }
}
    if(ev == tcpip_event) {
      if(uip_newdata()) {
        snmp_process_data();
      }
 * Copyright (C) 2019 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      An implementation of the Simple Network Management Protocol (RFC 3411-3418)
 * \defgroup snmp SNMP (Simple Network Management Protocol)
#include "contiki-net.h"
 * \defgroup SNMPDefine SNMP Defines
/** @} */

/**
 * \defgroup SNMPStructs SNMP Structs
 * @{
 */

   * @brief Union to hold the error status or the non repeaters
   *
   * @remarks A union was used since these values cannot co-exist
  union error_status_non_repeaters_u {
    /**
     * @brief The error status
     */
    uint32_t error_status;
    /**
     * @brief The non repeaters
     */
    uint32_t non_repeaters;
  } error_status_non_repeaters;
   * @brief Union to hold the error index or the max repetitions
   *
   * @remarks A union was used since these values cannot co-exist
  union error_index_max_repetitions_u {
    /**
     * @brief The error index
     */
    uint32_t error_index;
    /**
     * @brief The max repetitions
     */
    uint32_t max_repetitions;
  } error_index_max_repetitions;
   *
   * @remarks The length is configurable
  uint32_t oid[SNMP_MSG_OID_MAX_LEN];
  union snmp_varbind_val_u {
    struct snmp_varbind_string_s {
     * @brief A pointer to the beggining of a oid array
    uint32_t *oid;
/** @}*/

 * \defgroup SNMPFunctions SNMP Functions
 * @{
# Starting Contiki-NG native node
echo "Starting native node"
make -C $CONTIKI/examples/snmp-server > make.log 2> make.err
sudo $CONTIKI/examples/snmp-server/snmp-server.native > node.log 2> node.err &
CPID=$!
sleep 2

# Do Walk
echo "WALK!"
snmpwalk -t 4 -v 2c -c public udp6:[$IPADDR]:161 1 | tee $BASENAME.log
# Fetch snmpwalk status code (not $? because this is piped)
STATUS=${PIPESTATUS[0]}

echo "Closing native node"
sleep 2
kill_bg $CPID

if [ $STATUS -eq 0 ] ; then
  cp $BASENAME.log $BASENAME.testlog
  printf "%-32s TEST OK\n" "$BASENAME" | tee $BASENAME.testlog;
else
  echo "==== make.log ====" ; cat make.log;
  echo "==== make.err ====" ; cat make.err;
  echo "==== node.log ====" ; cat node.log;
  echo "==== node.err ====" ; cat node.err;
  echo "==== $BASENAME.log ====" ; cat $BASENAME.log;

  printf "%-32s TEST FAIL\n" "$BASENAME" | tee $BASENAME.testlog;
fi

rm make.log
rm make.err
rm node.log
rm node.err

Fixed code:
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
/*
 * In order to test SNMP compliance,
 * at least three OID's in the same package are necessary
 */
#define SNMP_CONF_MAX_NR_VALUES 3
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
sysDescr_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysDescr_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
sysObjectID_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysObjectID_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
  snmp_api_set_oid(varbind, oid, &sysObjectID_oid);
sysUpTime_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysUpTime_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
sysContact_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysContact_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
sysName_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysName_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
sysLocation_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysLocation_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
sysServices_handler(snmp_varbind_t *varbind, snmp_oid_t *oid);
sysServices_handler(snmp_varbind_t *varbind, snmp_oid_t *oid)
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the public API
snmp_api_set_string(snmp_varbind_t *varbind, snmp_oid_t *oid, char *string)
  memcpy(&varbind->oid, oid, sizeof(snmp_oid_t));
snmp_api_set_time_ticks(snmp_varbind_t *varbind, snmp_oid_t *oid, uint32_t integer)
  memcpy(&varbind->oid, oid, sizeof(snmp_oid_t));
  varbind->value_type = BER_DATA_TYPE_TIMETICKS;
snmp_api_set_oid(snmp_varbind_t *varbind, snmp_oid_t *oid, snmp_oid_t *ret_oid)
  memcpy(&varbind->oid, oid, sizeof(snmp_oid_t));
  varbind->value_type = BER_DATA_TYPE_OBJECT_IDENTIFIER;
  memcpy(&varbind->value.oid, ret_oid, sizeof(snmp_oid_t));
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the public API
 * \addtogroup SNMPAPI SNMP Public API
typedef void (*snmp_mib_resource_handler_t)(snmp_varbind_t *varbind, snmp_oid_t *oid);
  static snmp_oid_t name = { \
    .data = __VA_ARGS__, \
    .length = (sizeof((uint32_t[]){ __VA_ARGS__ }) / sizeof(uint32_t)) \
  };
  snmp_mib_resource_t name = { \
    NULL, \
    { \
      .data = __VA_ARGS__, \
      .length = (sizeof((uint32_t[]){ __VA_ARGS__ }) / sizeof(uint32_t)) \
    }, \
    handler \
  };
snmp_api_set_string(snmp_varbind_t *varbind, snmp_oid_t *oid, char *string);
snmp_api_set_time_ticks(snmp_varbind_t *varbind, snmp_oid_t *oid, uint32_t integer);
snmp_api_set_oid(snmp_varbind_t *varbind, snmp_oid_t *oid, snmp_oid_t *ret_oid);
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the BER encoding
static inline int
snmp_ber_encode_unsigned_integer(snmp_packet_t *snmp_packet, uint8_t type, uint32_t number)
  uint16_t original_out_len;
  original_out_len = snmp_packet->used;
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)number & 0xFF;
    snmp_packet->used++;
    /* I'm not sure why but on MSPGCC the >> 8 operation goes haywire here */
#ifdef __MSPGCC__
    number >>= 4;
    number >>= 4;
#else /* __MSPGCC__ */
#endif /* __MSPGCC__ */
  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used - original_out_len)) {
    return 0;
  }

  if(!snmp_ber_encode_type(snmp_packet, type)) {
    return 0;
  }
  return 1;
int
snmp_ber_encode_type(snmp_packet_t *snmp_packet, uint8_t type)
  if(snmp_packet->used == snmp_packet->max) {
    return 0;
  }
  *snmp_packet->out-- = type;
  snmp_packet->used++;

  return 1;
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_encode_length(snmp_packet_t *snmp_packet, uint16_t length)
{
  if(length > 0xFF) {
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)length & 0xFF;
    snmp_packet->used++;

    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)(length >> 8) & 0xFF;
    snmp_packet->used++;

    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = 0x82;
    snmp_packet->used++;
  } else if(length > 0x7F) {
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)length & 0xFF;
    snmp_packet->used++;
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }
    *snmp_packet->out-- = 0x81;
    snmp_packet->used++;
  } else {
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)length & 0x7F;
    snmp_packet->used++;
  }

  return 1;
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_encode_timeticks(snmp_packet_t *snmp_packet, uint32_t timeticks)
{
  return snmp_ber_encode_unsigned_integer(snmp_packet, BER_DATA_TYPE_TIMETICKS, timeticks);
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_encode_integer(snmp_packet_t *snmp_packet, uint32_t number)
{
  return snmp_ber_encode_unsigned_integer(snmp_packet, BER_DATA_TYPE_INTEGER, number);
int
snmp_ber_encode_string_len(snmp_packet_t *snmp_packet, const char *str, uint32_t length)
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)*str--;
    snmp_packet->used++;
  if(!snmp_ber_encode_length(snmp_packet, length)) {
    return 0;
  }
  if(!snmp_ber_encode_type(snmp_packet, BER_DATA_TYPE_OCTET_STRING)) {
    return 0;
  }
  return 1;
int
snmp_ber_encode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid)
  uint32_t val;
  uint16_t original_out_len;
  uint8_t pos;

  original_out_len = snmp_packet->used;

  pos = oid->length - 1;
  while(pos) {
    val = oid->data[pos];

    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)(val & 0x7F);
    snmp_packet->used++;
    val >>= 7;

    while(val) {
      if(snmp_packet->used == snmp_packet->max) {
        return 0;
      }

      *snmp_packet->out-- = (uint8_t)((val & 0x7F) | 0x80);
      snmp_packet->used++;

      val >>= 7;
    }
    pos--;
  }

  if(snmp_packet->used == snmp_packet->max) {
    return 0;
  }

  val = *(snmp_packet->out + 1) + 40 * oid->data[pos];
  snmp_packet->used--;
  snmp_packet->out++;

  if(snmp_packet->used == snmp_packet->max) {
    return 0;
  }

  *snmp_packet->out-- = (uint8_t)(val & 0x7F);
  snmp_packet->used++;

  val >>= 7;

  while(val) {
    if(snmp_packet->used == snmp_packet->max) {
      return 0;
    }

    *snmp_packet->out-- = (uint8_t)((val & 0x7F) | 0x80);
    snmp_packet->used++;

    val >>= 7;
  }

  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used - original_out_len)) {
    return 0;
  if(!snmp_ber_encode_type(snmp_packet, BER_DATA_TYPE_OBJECT_IDENTIFIER)) {
    return 0;
  }
  return 1;
int
snmp_ber_encode_null(snmp_packet_t *snmp_packet, uint8_t type)

  if(snmp_packet->used == snmp_packet->max) {
    return 0;
  *snmp_packet->out-- = 0x00;
  snmp_packet->used++;
  return snmp_ber_encode_type(snmp_packet, type);
static inline int
snmp_ber_decode_unsigned_integer(snmp_packet_t *snmp_packet, uint8_t expected_type, uint32_t *num)
  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
  }
  if(type != expected_type) {
    return 0;
  if(!snmp_ber_decode_length(snmp_packet, &len)) {
    return 0;
  }
  if(len > 4) {
    return 0;
  if(snmp_packet->used == 0) {
    return 0;
  *num = (uint32_t)(*snmp_packet->in++ & 0xFF);
  snmp_packet->used--;

    if(snmp_packet->used == 0) {
      return 0;
    }
    *num |= (uint8_t)(*snmp_packet->in++ & 0xFF);
    snmp_packet->used--;
  return 1;
int
snmp_ber_decode_type(snmp_packet_t *snmp_packet, uint8_t *type)
  if(snmp_packet->used == 0) {
    return 0;
  *type = *snmp_packet->in++;
  snmp_packet->used--;
  return 1;
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_decode_length(snmp_packet_t *snmp_packet, uint8_t *length)
{
  if(snmp_packet->used == 0) {
    return 0;
  *length = *snmp_packet->in++;
  snmp_packet->used--;
  return 1;
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_decode_timeticks(snmp_packet_t *snmp_packet, uint32_t *timeticks)
{
  return snmp_ber_decode_unsigned_integer(snmp_packet, BER_DATA_TYPE_TIMETICKS, timeticks);
int
snmp_ber_decode_integer(snmp_packet_t *snmp_packet, uint32_t *num)
{
  return snmp_ber_decode_unsigned_integer(snmp_packet, BER_DATA_TYPE_INTEGER, num);
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_decode_string_len_buffer(snmp_packet_t *snmp_packet, const char **str, uint32_t *length)
  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
  }
  if(type != BER_DATA_TYPE_OCTET_STRING) {
    return 0;
  if((*snmp_packet->in & 0x80) == 0) {

    if(snmp_packet->used == 0) {
      return 0;
    }

    *length = (uint32_t)*snmp_packet->in++;
    snmp_packet->used--;
    if(snmp_packet->used == 0) {
      return 0;
    }

    length_bytes = (uint8_t)(*snmp_packet->in++ & 0x7F);
    snmp_packet->used--;

      return 0;
    }

    if(snmp_packet->used == 0) {
      return 0;
    *length = (uint32_t)*snmp_packet->in++;
    snmp_packet->used--;


      if(snmp_packet->used == 0) {
        return 0;
      }

      *length |= *snmp_packet->in++;
      snmp_packet->used--;
  *str = (const char *)snmp_packet->in;

  if(snmp_packet->used == 0 || snmp_packet->used - *length <= 0) {
    return 0;
  }

  snmp_packet->used -= *length;
  snmp_packet->in += *length;

  return 1;
}
/*---------------------------------------------------------------------------*/
int
snmp_ber_decode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid)
{
  uint8_t *buf_end, type;
  uint8_t len, j;
  div_t first;

  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
  }

  if(type != BER_DATA_TYPE_OBJECT_IDENTIFIER) {
    return 0;
  }

  if(!snmp_ber_decode_length(snmp_packet, &len)) {
    return 0;
  }

  buf_end = snmp_packet->in + len;

  if(snmp_packet->used == 0) {
    return 0;
  }

  snmp_packet->used--;
  first = div(*snmp_packet->in++, 40);

  oid->length = 0;

  oid->data[oid->length++] = (uint32_t)first.quot;
  oid->data[oid->length++] = (uint32_t)first.rem;

  while(snmp_packet->in != buf_end) {
    if(oid->length >= SNMP_MSG_OID_MAX_LEN) {
      return 0;
    }
    if(snmp_packet->used == 0) {
      return 0;
    }
    oid->data[oid->length] = (uint32_t)(*snmp_packet->in & 0x7F);
    for(j = 0; j < 4; j++) {
      snmp_packet->used--;
      if((*snmp_packet->in++ & 0x80) == 0) {
        break;
      }

      if(snmp_packet->used == 0) {
        return 0;
      }

      oid->data[oid->length] <<= 7;
      oid->data[oid->length] |= (*snmp_packet->in & 0x7F);
    }

    oid->length++;
  }

  return 1;
int
snmp_ber_decode_null(snmp_packet_t *snmp_packet)
  if(snmp_packet->used == 0) {
    return 0;
  }

  snmp_packet->in++;
  snmp_packet->used--;

  if(snmp_packet->used == 0) {
    return 0;
  }
  snmp_packet->in++;
  snmp_packet->used--;
  return 1;
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the BER encoding
/**
 * \addtogroup SNMPInternal SNMP Internal API
 * @{
 *
 * This group contains all the functions that can be used inside the OS level.
 */

/**
 * \addtogroup SNMPBER SNMP BER
 * @{
 *
 * This group contains the BER implementation
 */

/**
 * @brief End-of-Content
 *
 */
#define BER_DATA_TYPE_EOC                   0x00

/**
 * @brief Integer
 *
 */
#define BER_DATA_TYPE_INTEGER               0x02

/**
 * @brief Octet String
 *
 */
#define BER_DATA_TYPE_OCTET_STRING          0x04

/**
 * @brief Null
 *
 */
#define BER_DATA_TYPE_NULL                  0x05

/**
 * @brief Object Identifier
 *
 */
#define BER_DATA_TYPE_OBJECT_IDENTIFIER     0x06

/**
 * @brief Sequence
 *
 */
#define BER_DATA_TYPE_SEQUENCE              0x30

/**
 * @brief TimeTicks
 *
 */
#define BER_DATA_TYPE_TIMETICKS             0x43

/**
 * @brief No Such Instance
 *
 */
#define BER_DATA_TYPE_NO_SUCH_INSTANCE      0x81

/**
 * @brief End of MIB View
 *
 */
#define BER_DATA_TYPE_END_OF_MIB_VIEW       0x82

/**
 * @brief PDU Get Request
 *
 */
#define BER_DATA_TYPE_PDU_GET_REQUEST       0xA0

/**
 * @brief PDU Get Next Request
 *
 */
#define BER_DATA_TYPE_PDU_GET_NEXT_REQUEST  0xA1

/**
 * @brief PDU Get Reponse
 *
 */
#define BER_DATA_TYPE_PDU_GET_RESPONSE      0xA2

/**
 * @brief PDU Set Request
 *
 */
#define BER_DATA_TYPE_PDU_SET_REQUEST       0xA3

/**
 * @brief PDU Trap
 *
 */
#define BER_DATA_TYPE_PDU_TRAP              0xA4

/**
 * @brief PDU Get Bulk
 *
 */
#define BER_DATA_TYPE_PDU_GET_BULK          0xA5
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_encode_type(snmp_packet_t *snmp_packet, uint8_t type);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_encode_length(snmp_packet_t *snmp_packet, uint16_t length);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_encode_integer(snmp_packet_t *snmp_packet, uint32_t integer);
 * @brief Encodes a timeticks
 * @param snmp_packet A pointer to the snmp packet
 * @param timeticks A TimeTicks
 * @return 0 if error or 1 if success
int
snmp_ber_encode_timeticks(snmp_packet_t *snmp_packet, uint32_t timeticks);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
 */
int
snmp_ber_encode_string_len(snmp_packet_t *snmp_packet, const char *str, uint32_t length);

/**
 * @brief Encodes a Oid
 *
 * @param snmp_packet A pointer to the snmp packet
 * @param oid A OID
 *
 * @return 0 if error or 1 if success
int
snmp_ber_encode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_encode_null(snmp_packet_t *snmp_packet, uint8_t type);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_decode_type(snmp_packet_t *snmp_packet, uint8_t *type);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_decode_length(snmp_packet_t *snmp_packet, uint8_t *length);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_decode_integer(snmp_packet_t *snmp_packet, uint32_t *integer);
 * @brief Decodes a timeticks
 * @param snmp_packet A pointer to the snmp packet
 * @param timeticks A pointer to the timeticks
 * @return 0 if error or 1 if success
int
snmp_ber_decode_timeticks(snmp_packet_t *snmp_packet, uint32_t *timeticks);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_decode_string_len_buffer(snmp_packet_t *snmp_packet, const char **str, uint32_t *length);
 * @param snmp_packet A pointer to the snmp packet
 * @return 0 if error or 1 if success
int
snmp_ber_decode_null(snmp_packet_t *snmp_packet);

/**
 * @brief Decodes an OID
 *
 * @param snmp_packet  pointer to the snmp packet
 * @param oid A pointer to the OID
 *
 * @return 0 if error or 1 if success
 */
int
snmp_ber_decode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid);

/** @} */

/** @} */

 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the configurable macros
 * \addtogroup SNMPInternal SNMP Internal API
 *
 * This group contains all the functions that can be used inside the OS level.
 */

/**
 * \addtogroup SNMPConfs SNMP Configurable
 * @{
 *
 * This group contains all the defines that can be configurable following the Contiki standard
#if SNMP_CONF_MSG_OID_MAX_LEN > 128
#error "OID is limited to 128 in the standard"
#endif
#if SNMP_CONF_MAX_NR_VALUES > 255
#error "Number of OID's per packet is limited to 255 in this implementation"
#endif
#error "SNMP_CONF_MAX_PACKET_SIZE is obsolete. Use UIP_CONF_BUFFER_SIZE"
#endif /* SNMP_CONF_MAX_PACKET_SIZE */
/** @} */

/** @} */

 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the protocol engine
#include "snmp-ber.h"
static inline int
snmp_engine_get(snmp_header_t *header, snmp_varbind_t *varbinds)
  uint8_t i;
  i = 0;
  while(varbinds[i].value_type != BER_DATA_TYPE_EOC && i < SNMP_MAX_NR_VALUES) {
    resource = snmp_mib_find(&varbinds[i].oid);
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = i + 1;
        (&varbinds[i])->value_type = BER_DATA_TYPE_NO_SUCH_INSTANCE;
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = 0;
      resource->handler(&varbinds[i], &resource->oid);

    i++;
static inline int
snmp_engine_get_next(snmp_header_t *header, snmp_varbind_t *varbinds)
  uint8_t i;
  i = 0;
  while(varbinds[i].value_type != BER_DATA_TYPE_EOC && i < SNMP_MAX_NR_VALUES) {
    resource = snmp_mib_find_next(&varbinds[i].oid);
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = i + 1;
        (&varbinds[i])->value_type = BER_DATA_TYPE_END_OF_MIB_VIEW;
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = 0;
      resource->handler(&varbinds[i], &resource->oid);

    i++;
static inline int
snmp_engine_get_bulk(snmp_header_t *header, snmp_varbind_t *varbinds)
  snmp_oid_t oids[SNMP_MAX_NR_VALUES];
  uint32_t j, original_varbinds_length;
  uint8_t i, varbinds_length;
  original_varbinds_length = 0;
  while(varbinds[original_varbinds_length].value_type != BER_DATA_TYPE_EOC && original_varbinds_length < SNMP_MAX_NR_VALUES) {
    memcpy(&oids[original_varbinds_length], &varbinds[original_varbinds_length].oid, sizeof(snmp_oid_t));
    original_varbinds_length++;
  varbinds_length = 0;
    if(i >= header->non_repeaters) {
    resource = snmp_mib_find_next(&oids[i]);
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = i + 1;
        (&varbinds[i])->value_type = BER_DATA_TYPE_END_OF_MIB_VIEW;
        header->error_status = SNMP_STATUS_NO_SUCH_NAME;
        header->error_index = 0;
      if(varbinds_length < SNMP_MAX_NR_VALUES) {
        resource->handler(&varbinds[varbinds_length], &resource->oid);
        (varbinds_length)++;
      } else {
        return -1;
  for(i = 0; i < header->max_repetitions; i++) {
    for(j = header->non_repeaters; j < original_varbinds_length; j++) {
      resource = snmp_mib_find_next(&oids[j]);
          header->error_status = SNMP_STATUS_NO_SUCH_NAME;
          header->error_index = varbinds_length + 1;
          if(varbinds_length < SNMP_MAX_NR_VALUES) {
            (&varbinds[varbinds_length])->value_type = BER_DATA_TYPE_END_OF_MIB_VIEW;
            memcpy(&varbinds[varbinds_length].oid, &oids[j], sizeof(snmp_oid_t));
            (varbinds_length)++;
          } else {
            return -1;
          header->error_status = SNMP_STATUS_NO_SUCH_NAME;
          header->error_index = 0;
        if(varbinds_length < SNMP_MAX_NR_VALUES) {
          resource->handler(&varbinds[varbinds_length], &resource->oid);
          (varbinds_length)++;
          memcpy(&oids[j], &resource->oid, sizeof(snmp_oid_t));
        } else {
          return -1;
int
snmp_engine(snmp_packet_t *snmp_packet)
  snmp_header_t header;
  snmp_varbind_t varbinds[SNMP_MAX_NR_VALUES];

  memset(&header, 0, sizeof(header));
  memset(varbinds, 0, sizeof(varbinds));
  if(!snmp_message_decode(snmp_packet, &header, varbinds)) {
    return 0;
      return 0;
  case BER_DATA_TYPE_PDU_GET_REQUEST:
    if(snmp_engine_get(&header, varbinds) == -1) {
      return 0;
  case BER_DATA_TYPE_PDU_GET_NEXT_REQUEST:
    if(snmp_engine_get_next(&header, varbinds) == -1) {
      return 0;
  case BER_DATA_TYPE_PDU_GET_BULK:
    if(snmp_engine_get_bulk(&header, varbinds) == -1) {
      return 0;
    return 0;
  header.pdu_type = BER_DATA_TYPE_PDU_GET_RESPONSE;
  return snmp_message_encode(snmp_packet, &header, varbinds);
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the protocol engine
/**
 * \addtogroup SNMPInternal SNMP Internal API
 * @{
 *
 * This group contains all the functions that can be used inside the OS level.
 */
 * \addtogroup SNMPEngine SNMP Engine
 * @{
 * This group contains the Engine implementation
 */

/**
 * @brief Process the SNMP packet and prepares the response
 * @param snmp_packet A pointer to the snmp packet
int
snmp_engine(snmp_packet_t *snmp_packet);

/** @} */

/** @} */
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the messages
int
snmp_message_encode(snmp_packet_t *snmp_packet, snmp_header_t *header, snmp_varbind_t *varbinds)
  uint32_t last_out_len;
  for(i = SNMP_MAX_NR_VALUES - 1; i >= 0; i--) {
    if(varbinds[i].value_type == BER_DATA_TYPE_EOC) {
      continue;
    }
    last_out_len = snmp_packet->used;
    switch(varbinds[i].value_type) {
      if(!snmp_ber_encode_integer(snmp_packet, varbinds[i].value.integer)) {
        LOG_DBG("Could not encode integer type\n");
        return 0;
      }
    case BER_DATA_TYPE_TIMETICKS:
      if(!snmp_ber_encode_timeticks(snmp_packet, varbinds[i].value.integer)) {
        LOG_DBG("Could not encode timeticks type\n");
        return 0;
      }
      if(!snmp_ber_encode_string_len(snmp_packet, varbinds[i].value.string.string, varbinds[i].value.string.length)) {
        LOG_DBG("Could not encode octet string type\n");
        return 0;
      }
    case BER_DATA_TYPE_OBJECT_IDENTIFIER:
      if(!snmp_ber_encode_oid(snmp_packet, &varbinds[i].value.oid)) {
        LOG_DBG("Could not encode oid type\n");
        return 0;
      }
    case BER_DATA_TYPE_NO_SUCH_INSTANCE:
    case BER_DATA_TYPE_END_OF_MIB_VIEW:
      if(!snmp_ber_encode_null(snmp_packet, varbinds[i].value_type)) {
        LOG_DBG("Could not encode null type\n");
        return 0;
      }
      LOG_DBG("Could not encode invlid type\n");
      return 0;
    }

    if(!snmp_ber_encode_oid(snmp_packet, &varbinds[i].oid)) {
      LOG_DBG("Could not encode oid\n");
      return 0;
    }

    if(!snmp_ber_encode_length(snmp_packet, (snmp_packet->used - last_out_len))) {
      LOG_DBG("Could not encode length\n");
      return 0;
    if(!snmp_ber_encode_type(snmp_packet, BER_DATA_TYPE_SEQUENCE)) {
      LOG_DBG("Could not encode type\n");
      return 0;
    }
  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used)) {
    LOG_DBG("Could not encode length\n");
    return 0;
  }
  if(!snmp_ber_encode_type(snmp_packet, BER_DATA_TYPE_SEQUENCE)) {
    LOG_DBG("Could not encode type\n");
    return 0;
  }
  switch(header->pdu_type) {
  case BER_DATA_TYPE_PDU_GET_BULK:
    if(!snmp_ber_encode_integer(snmp_packet, header->max_repetitions)) {
      LOG_DBG("Could not encode max repetition\n");
      return 0;
    }

    if(!snmp_ber_encode_integer(snmp_packet, header->non_repeaters)) {
      LOG_DBG("Could not encode non repeaters\n");
      return 0;
    }
    break;
  default:
    if(!snmp_ber_encode_integer(snmp_packet, header->error_index)) {
      LOG_DBG("Could not encode error index\n");
      return 0;
    }

    if(!snmp_ber_encode_integer(snmp_packet, header->error_status)) {
      LOG_DBG("Could not encode error status\n");
      return 0;
    }
    break;
  if(!snmp_ber_encode_integer(snmp_packet, header->request_id)) {
    LOG_DBG("Could not encode request id\n");
    return 0;
  }
  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used)) {
    LOG_DBG("Could not encode length\n");
    return 0;
  }
  if(!snmp_ber_encode_type(snmp_packet, header->pdu_type)) {
    LOG_DBG("Could not encode pdu type\n");
    return 0;
  }
  if(!snmp_ber_encode_string_len(snmp_packet, header->community.community, header->community.length)) {
    LOG_DBG("Could not encode community\n");
    return 0;
  }

  if(!snmp_ber_encode_integer(snmp_packet, header->version)) {
    LOG_DBG("Could not encode version\n");
    return 0;
  }

  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used)) {
    LOG_DBG("Could not encode length\n");
    return 0;
  }

  if(!snmp_ber_encode_type(snmp_packet, BER_DATA_TYPE_SEQUENCE)) {
    LOG_DBG("Could not encode type\n");
    return 0;
  }

  /* Move the pointer to the last position */
  snmp_packet->out++;
  return 1;
int
snmp_message_decode(snmp_packet_t *snmp_packet, snmp_header_t *header, snmp_varbind_t *varbinds)
  uint8_t type, len, i;
  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
    return 0;
  if(!snmp_ber_decode_length(snmp_packet, &len)) {
    return 0;
  if(!snmp_ber_decode_integer(snmp_packet, &header->version)) {
    return 0;
  switch(header->version) {
  case SNMP_VERSION_1:
  case SNMP_VERSION_2C:
    break;
  default:
    LOG_DBG("Invalid version\n");
    return 0;
  if(!snmp_ber_decode_string_len_buffer(snmp_packet, &header->community.community, &header->community.length)) {
    LOG_DBG("Could not decode community\n");
    return 0;
  if(!snmp_ber_decode_type(snmp_packet, &header->pdu_type)) {
    LOG_DBG("Could not decode pdu type\n");
    return 0;
  switch(header->pdu_type) {
  case BER_DATA_TYPE_PDU_GET_REQUEST:
  case BER_DATA_TYPE_PDU_GET_NEXT_REQUEST:
  case BER_DATA_TYPE_PDU_GET_RESPONSE:
  case BER_DATA_TYPE_PDU_SET_REQUEST:
  case BER_DATA_TYPE_PDU_GET_BULK:
    break;
  default:
    LOG_DBG("Invalid version\n");
    return 0;
  if(!snmp_ber_decode_length(snmp_packet, &len)) {
    return 0;
  if(!snmp_ber_decode_integer(snmp_packet, &header->request_id)) {
    return 0;
  switch(header->pdu_type) {
  case BER_DATA_TYPE_PDU_GET_BULK:
    if(!snmp_ber_decode_integer(snmp_packet, &header->non_repeaters)) {
      LOG_DBG("Could not decode non repeaters\n");
      return 0;
    if(!snmp_ber_decode_integer(snmp_packet, &header->max_repetitions)) {
      LOG_DBG("Could not decode max repetition\n");
      return 0;
    break;
  default:
    if(!snmp_ber_decode_integer(snmp_packet, &header->error_status)) {
      return 0;
    if(!snmp_ber_decode_integer(snmp_packet, &header->error_index)) {
      return 0;
    break;
  if(!snmp_ber_decode_type(snmp_packet, &type)) {
    return 0;
    return 0;
  if(!snmp_ber_decode_length(snmp_packet, &len)) {
    return 0;
  for(i = 0; snmp_packet->used > 0; ++i) {
    if(i >= SNMP_MAX_NR_VALUES) {
      LOG_DBG("OID's overflow\n");
      return 0;
    if(!snmp_ber_decode_type(snmp_packet, &type)) {
      return 0;
      return 0;
    if(!snmp_ber_decode_length(snmp_packet, &len)) {
      return 0;
    if(!snmp_ber_decode_oid(snmp_packet, &varbinds[i].oid)) {
      return 0;
    varbinds[i].value_type = *snmp_packet->in;
      if(!snmp_ber_decode_integer(snmp_packet, &varbinds[i].value.integer)) {
        LOG_DBG("Could not decode integer type\n");
        return 0;
      }
    case BER_DATA_TYPE_TIMETICKS:
      if(!snmp_ber_decode_timeticks(snmp_packet, &varbinds[i].value.integer)) {
        LOG_DBG("Could not decode timeticks type\n");
        return 0;
      }
      if(!snmp_ber_decode_string_len_buffer(snmp_packet, &varbinds[i].value.string.string, &varbinds[i].value.string.length)) {
        LOG_DBG("Could not decode octed string type\n");
        return 0;
      }
      if(!snmp_ber_decode_null(snmp_packet)) {
        LOG_DBG("Could not decode null type\n");
        return 0;
      }
      return 0;
  return 1;
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the messages
/**
 * \addtogroup SNMPInternal SNMP Internal API
 * @{
 *
 * This group contains all the functions that can be used inside the OS level.
 */
#include "snmp.h"
/**
 * \addtogroup SNMPMessage SNMP Message
 * @{
 *
 * This group contains the Message implementation
 */
 * @param snmp_packet A pointer to the snmp packet
int
snmp_message_encode(snmp_packet_t *snmp_packet, snmp_header_t *header, snmp_varbind_t *varbinds);
 * @param snmp_packet A pointer to the snmp packet
int
snmp_message_decode(snmp_packet_t *snmp_packet, snmp_header_t *header, snmp_varbind_t *varbinds);

/** @} */

/** @} */
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the MIB
/*---------------------------------------------------------------------------*/
/**
 * @brief Compares to oids
 *
 * @param oid1 First Oid
 * @param oid2 Second Oid
 *
 * @return < 0 if oid1 < oid2, > 0 if oid1 > oid2 and 0 if they are equal
 */
static inline int
snmp_mib_cmp_oid(snmp_oid_t *oid1, snmp_oid_t *oid2)
{
  uint8_t i;

  i = 0;
  while(i < oid1->length && i < oid2->length) {
    if(oid1->data[i] != oid2->data[i]) {
      if(oid1->data[i] < oid2->data[i]) {
        return -1;
      }
      return 1;
    }
    i++;
  }

  if(i == oid1->length &&
     i < oid2->length) {
    return -1;
  }

  if(i < oid1->length &&
     i == oid2->length) {
    return 1;
  }

  return 0;
}
/*---------------------------------------------------------------------------*/
snmp_mib_find(snmp_oid_t *oid)
    if(!snmp_mib_cmp_oid(oid, &resource->oid)) {
/*---------------------------------------------------------------------------*/
snmp_mib_find_next(snmp_oid_t *oid)
    if(snmp_mib_cmp_oid(&resource->oid, oid) > 0) {
/*---------------------------------------------------------------------------*/
  uint8_t i;
    if(snmp_mib_cmp_oid(&resource->oid, &new_resource->oid) > 0) {
  if(LOG_DBG_ENABLED) {
    /*
     * We print the entire resource table
     */
    LOG_DBG("Table after insert.\n");
    for(resource = list_head(snmp_mib);
        resource; resource = resource->next) {

      i = 0;
      LOG_DBG("{");
      while(i < resource->oid.length) {
        LOG_DBG_("%lu", (unsigned long)resource->oid.data[i]);
        i++;
        if(i < resource->oid.length) {
          LOG_DBG_(".");
        }
      }
      LOG_DBG_("}\n");
    }
/*---------------------------------------------------------------------------*/
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the MIB
/**
 * \addtogroup SNMPInternal SNMP Internal API
 * @{
 *
 * This group contains all the functions that can be used inside the OS level.
 */

/**
 * \addtogroup SNMPMIB SNMP MIB
 * @{
 *
 * This group contains the MIB implementation
 */

typedef void (*snmp_mib_resource_handler_t)(snmp_varbind_t *varbind, snmp_oid_t *oid);
   * @brief A OID struct
  snmp_oid_t oid;
snmp_mib_find(snmp_oid_t *oid);
snmp_mib_find_next(snmp_oid_t *oid);
/** @} */

/** @} */


 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the process
#include "contiki-net.h"
static struct uip_udp_conn *snmp_udp_conn;
  snmp_packet_t snmp_packet;

    if(ev != tcpip_event) {
      continue;
    }

    if(!uip_newdata()) {
      continue;

    LOG_DBG("receiving UDP datagram from [");
    LOG_DBG_6ADDR(&UIP_IP_BUF->srcipaddr);
    LOG_DBG_("]:%u", uip_ntohs(UIP_UDP_BUF->srcport));
    LOG_DBG_(" Length: %u\n", uip_datalen());

    /* Setup SNMP packet */
    snmp_packet.in = (uint8_t *)uip_appdata;
    snmp_packet.used = uip_datalen();

    snmp_packet.out = (uint8_t *)(uip_appdata + UIP_BUFSIZE - UIP_IPUDPH_LEN);
    snmp_packet.max = UIP_BUFSIZE - UIP_IPUDPH_LEN;

    /* Handle the request */
    if(!snmp_engine(&snmp_packet)) {
      LOG_DBG("Error while handling the request\n");
      continue;
    }

    LOG_DBG("Sending response\n");
    /* Send the response */
    uip_udp_packet_sendto(snmp_udp_conn, snmp_packet.out, snmp_packet.used, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport);
 * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
 *      SNMP Implementation of the process
 * \addtogroup snmp An implementation of SNMP
/**
 * \addtogroup SNMPInternal SNMP Internal API
 * @{
 *
 * This group contains all the functions that can be used inside the OS level.
 */

 * \addtogroup SNMPCore SNMP Core
 *
 * This group contains the SNMP MIB implementation
   * @brief The error status
  uint32_t error_status;
   * @brief The non repeaters
  uint32_t non_repeaters;
  /**
   * @brief The error index
   */
  uint32_t error_index;
  /**
   * @brief The max repetitions
   */
  uint32_t max_repetitions;
/**
 * @brief The OID struct
 */
typedef struct snmp_oid_s {
  /**
   * @brief The OID
   */
  uint32_t data[SNMP_MSG_OID_MAX_LEN];
  /**
   * @brief The OID length
   *
   */
  uint8_t length;
} snmp_oid_t;

  snmp_oid_t oid;
  union {
    struct {
     * @brief The OID value
    snmp_oid_t oid;
 * @brief The packet struct
 *
typedef struct {
  /**
   * @brief The number os bytes used
   *
   */
  uint16_t used;
  /**
   * @brief The maximum number of bytes
   *
   */
  uint16_t max;
  /**
   * @brief The pointer used for the incoming packet
   *
   */
  uint8_t *in;
  /**
   * @brief The pointer used for the outgoing packet
   *
   */
  uint8_t *out;
} snmp_packet_t;
/** @}*/



test_handler () {
  # Starting Contiki-NG native node
  make -C $CONTIKI/examples/snmp-server > make.log 2> make.err
  sudo $CONTIKI/examples/snmp-server/snmp-server.native > node.log 2> node.err &
  CPID=$!
  sleep 2

  $1 2>&1 | grep -z -E "$2" >> $BASENAME.log 2>&1 
  STATUS=$?
  
  sleep 2
  kill_bg $CPID > /dev/null 2>&1
  wait $CPID > /dev/null 2>&1

  if [ $STATUS -eq 0 ] ; then
    cp $BASENAME.log $BASENAME.testlog
    printf "%-32s TEST OK\n" "$BASENAME" | tee $BASENAME.testlog;
  else
    echo "==== make.log ====" ; cat make.log;
    echo "==== make.err ====" ; cat make.err;
    echo "==== node.log ====" ; cat node.log;
    echo "==== node.err ====" ; cat node.err;
    echo "==== $BASENAME.log ====" ; cat $BASENAME.log;
    
    printf "%-32s TEST FAIL\n" "$BASENAME" | tee $BASENAME.testlog;
  fi

  rm make.log
  rm make.err
  rm node.log
  rm node.err
}

# v1
## snmpget - pass
test_handler "snmpget -t2 -v1 -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.0" "iso\.3\.6\.1\.2\.1\.1\.1\.0"
## snmpwalk - pass
test_handler "snmpwalk -t2 -v1 -c public udp6:[$IPADDR]:161 1" "iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.2\.0.*iso\.3\.6\.1\.2\.1\.1\.3\.0.*iso\.3\.6\.1\.2\.1\.1\.4\.0.*iso\.3\.6\.1\.2\.1\.1\.5\.0.*iso\.3\.6\.1\.2\.1\.1\.6\.0.*iso\.3\.6\.1\.2\.1\.1\.7\.0.*End of MIB"

## snmpget - fail - noSuchName
test_handler "snmpget -t2 -v1 -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1" ".*noSuchName.*"
## snmpget - fail - timeout - 16 Ids in OID
test_handler "snmpget -t2 -v1 -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.1.3.6.1.2.1.1.1.1" "Timeout.*"
## snmpget - fail - timeout - 4 OIDs
test_handler "snmpget -t2 -v1 -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0" "Timeout.*"

# v2
## snmpget - pass
test_handler "snmpget -t2 -v2c -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.0" "iso\.3\.6\.1\.2\.1\.1\.1\.0"
## snmpwalk - pass
test_handler "snmpwalk -t2 -v2c -c public udp6:[$IPADDR]:161 1" "iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.2\.0.*iso\.3\.6\.1\.2\.1\.1\.3\.0.*iso\.3\.6\.1\.2\.1\.1\.4\.0.*iso\.3\.6\.1\.2\.1\.1\.5\.0.*iso\.3\.6\.1\.2\.1\.1\.6\.0.*iso\.3\.6\.1\.2\.1\.1\.7\.0.*iso\.3\.6\.1\.2\.1\.1\.7\.0"
## snmpbulkget two non-repeater - pass
test_handler "snmpbulkget -v2c -Cn2 -c public udp6:[$IPADDR]:161 1 1" "iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.1\.0"
## snmpbulkget two max-repetitions - pass
test_handler "snmpbulkget -t2 -v2c -Cr2 -c public udp6:[$IPADDR]:161 1" "iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.2\.0"
## snmpbulkget one non-repeater and two max-repetitions - pass
test_handler "snmpbulkget -t2 -v2c -Cn1 -Cr2 -c public udp6:[$IPADDR]:161 1 1" "iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.1\.0.*iso\.3\.6\.1\.2\.1\.1\.2\.0"

## snmpget - fail - noSuchName
test_handler "snmpget -t2 -v2c -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1" ".*No Such Instance currently.*"
## snmpget - fail - timeout - 16 Ids in OID
test_handler "snmpget -t2 -v2c -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.1.3.6.1.2.1.1.1.1" "Timeout.*"
## snmpget - fail - timeout - 4 OIDs
test_handler "snmpget -t2 -v2c -c public udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.1.0" "Timeout.*"

#v3
## snmpget - fail - timeout - v3 not implemented
test_handler "snmpget -t2 -v3 -l authPriv -u snmp-poller -a SHA -A \"PASSWORD1\" -x AES -X \"PASSWORD1\" udp6:[$IPADDR]:161 1.3.6.1.2.1.1.1.0" ".*Timeout.*"