From 557045da3f48f63b39dc95efc96f536c0d751092 Mon Sep 17 00:00:00 2001
From: Marco <marco.veeneman@schleifenbauer.eu>
Date: Wed, 1 Mar 2017 16:13:45 +0100
Subject: [PATCH] Added handling invalid packets in SNMPv3.

---
 src/apps/snmp/snmp_core_priv.h    |   7 +
 src/apps/snmp/snmp_msg.c          | 273 +++++++++++++++++++++++++++++++-------
 src/apps/snmp/snmp_msg.h          |   5 +
 src/apps/snmp/snmpv3_priv.h       |   5 +
 src/include/lwip/apps/snmp_core.h |   8 ++
 5 files changed, 253 insertions(+), 45 deletions(-)

diff --git a/src/apps/snmp/snmp_core_priv.h b/src/apps/snmp/snmp_core_priv.h
index 5552177..51704fc 100644
--- a/src/apps/snmp/snmp_core_priv.h
+++ b/src/apps/snmp/snmp_core_priv.h
@@ -55,6 +55,13 @@ extern "C" {
  */
 #define SNMP_ERR_TOOBIG               1
 #define SNMP_ERR_AUTHORIZATIONERROR   16
+
+#define SNMP_ERR_UNKNOWN_ENGINEID     30
+#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
+#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
+#define SNMP_ERR_NOTINTIMEWINDOW      33
+#define SNMP_ERR_DECRYIPTION_ERROR    34
+
 #define SNMP_ERR_NOSUCHOBJECT         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
 #define SNMP_ERR_ENDOFMIBVIEW         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
 
diff --git a/src/apps/snmp/snmp_msg.c b/src/apps/snmp/snmp_msg.c
index 7e36f29..c194ae5 100644
--- a/src/apps/snmp/snmp_msg.c
+++ b/src/apps/snmp/snmp_msg.c
@@ -294,6 +294,70 @@ snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t por
           err = snmp_process_set_request(&request);
         }
       }
+      else {
+        struct snmp_varbind vb;
+
+        vb.next = NULL;
+        vb.prev = NULL;
+        vb.type = SNMP_ASN1_TYPE_COUNTER32;
+        vb.value_len = sizeof(u32_t);
+
+        switch (request.error_status) {
+        case SNMP_ERR_AUTHORIZATIONERROR:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 } };
+            vb.value = &snmp_stats.wrongdigests;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        case SNMP_ERR_UNKNOWN_ENGINEID:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 } };
+            vb.value = &snmp_stats.unknownengineids;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        case SNMP_ERR_UNKNOWN_SECURITYNAME:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 } };
+            vb.value = &snmp_stats.unknownusernames;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        case SNMP_ERR_UNSUPPORTED_SECLEVEL:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 } };
+            vb.value = &snmp_stats.unsupportedseclevels;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        case SNMP_ERR_NOTINTIMEWINDOW:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 } };
+            vb.value = &snmp_stats.notintimewindows;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        case SNMP_ERR_DECRYIPTION_ERROR:
+          {
+            struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 } };
+            vb.value = &snmp_stats.decryptionerrors;
+            MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
+          }
+          break;
+        default:
+          /* Unknown or unhandled error_status */
+          err = ERR_ARG;
+        }
+
+        if (err == ERR_OK) {
+          snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
+          request.error_status = SNMP_ERR_NOERROR;
+        }
+
+        request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
+        request.request_id = request.msg_id;
+      }
 
       if (err == ERR_OK) {
         err = snmp_complete_outbound_frame(&request);
@@ -703,6 +767,9 @@ snmp_parse_inbound_frame(struct snmp_request *request)
   s32_t parent_tlv_value_len;
   s32_t s32_value;
   err_t err;
+#if LWIP_SNMP_V3
+  u8_t auth, priv;
+#endif
 
   IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
   
@@ -828,7 +895,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
     IF_PARSE_ASSERT(parent_tlv_value_len > 0);
     IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
-    /* @todo: Implement time window checking */
 
     /* msgUserName */
     IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@@ -839,8 +905,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
         &u16_value, SNMP_V3_MAX_USER_LENGTH));
     request->msg_user_name_len = (u8_t)u16_value;
-    /* @todo: Implement unknown user error response */
-    IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL));
 
     /* msgAuthenticationParameters */
     memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
@@ -850,12 +914,92 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     IF_PARSE_ASSERT(parent_tlv_value_len > 0);
     /* Remember position */
     inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
-    LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
+	LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
     /* Read auth parameters */
-    IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    //IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
     IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
         &u16_value, tlv.value_len));
+    request->msg_authentication_parameters_len = (u8_t)u16_value;
+
+    /* msgPrivacyParameters */
+    memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+        &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+    request->msg_privacy_parameters_len = (u8_t)u16_value;
+
+    /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames) */
+    // 1) securityParameters was correctly serialized if we reach here.
+    // 2) securityParameters are already cached.
+    // 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
+    //    b) https://tools.ietf.org/html/rfc3414#section-7
+    {
+      const char *eid;
+      u8_t eid_len;
+
+      snmpv3_get_engine_id(&eid, &eid_len);
+
+      if ((request->msg_authoritative_engine_id_len == 0) ||
+    	  (request->msg_authoritative_engine_id_len != eid_len) ||
+    	  (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
+        snmp_stats.unknownengineids++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
+        return ERR_OK;
+      }
+    }
 
+    // 4) verify username
+    if(snmpv3_get_user((char*)request->msg_user_name, &auth, NULL, &priv, NULL)) {
+      snmp_stats.unknownusernames++;
+      request->msg_flags = 0; // noauthnopriv
+      request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
+      return ERR_OK;
+    }
+
+    // 5) verify security level
+    switch(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
+    case SNMP_V3_NOAUTHNOPRIV:
+      if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+        /* Invalid security level for user */
+        snmp_stats.unsupportedseclevels++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+        return ERR_OK;
+      }
+      break;
+#if LWIP_SNMP_V3_CRYPTO
+    case SNMP_V3_AUTHNOPRIV:
+      if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+        /* Invalid security level for user */
+        snmp_stats.unsupportedseclevels++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+        return ERR_OK;
+      }
+      break;
+    case SNMP_V3_AUTHPRIV:
+      if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
+        /* Invalid security level for user */
+        snmp_stats.unsupportedseclevels++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+        return ERR_OK;
+      }
+      break;
+#endif
+    default:
+      snmp_stats.unsupportedseclevels++;
+      request->msg_flags = 0; // noauthnopriv
+      request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+      return ERR_OK;
+    }
+
+    // 6) if securitylevel specifies authentication, authenticate message.
 #if LWIP_SNMP_V3_CRYPTO
     if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
       const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
@@ -864,40 +1008,67 @@ snmp_parse_inbound_frame(struct snmp_request *request)
       u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
       struct snmp_pbuf_stream auth_stream;
 
+      if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
+        snmp_stats.wrongdigests++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+        return ERR_OK;
+      }
+
       /* Rewind stream */
-      IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
-      IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset));
+      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+      IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
       /* Set auth parameters to zero for verification */
-      IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len));
+      IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
 
       /* Verify authentication */
       IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
 
       IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
       IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
-      /* @todo: Implement error response */
-      IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
-    }
-#else
-    /* Ungraceful exit if we encounter cryptography and don't support it.
-     * @todo: Implement error response
-     */
-    IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)));
-#endif
 
-    /* msgPrivacyParameters */
-    memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+      if(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
+        snmp_stats.wrongdigests++;
+        request->msg_flags = 0; // noauthnopriv
+        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+        return ERR_OK;
+      }
 
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
-        &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+      // 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime
+      {
+        u32_t boots = snmpv3_get_engine_boots_internal();
+        if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
+          snmp_stats.notintimewindows++;
+          request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
+          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+          return ERR_OK;
+        }
+      }
+      {
+        u32_t time = snmpv3_get_engine_time_internal();
+        if (request->msg_authoritative_engine_time > time) {
+          snmp_stats.notintimewindows++;
+          request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
+          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+          return ERR_OK;
+        }
+        else if (time > 150) {
+          if (request->msg_authoritative_engine_time < time - 150) {
+            snmp_stats.notintimewindows++;
+            request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
+            request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+            return ERR_OK;
+          }
+        }
+      }
+    }
+#endif
 
+    // 8) if securitylevel specifies privacy, decrypt message.
 #if LWIP_SNMP_V3_CRYPTO
-    /* Decrypt message */
     if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      /* Decrypt message */
+
       u8_t key[20];
       u8_t algo;
 
@@ -907,11 +1078,20 @@ snmp_parse_inbound_frame(struct snmp_request *request)
       IF_PARSE_ASSERT(parent_tlv_value_len > 0);
 
       IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
-      IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+      if(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
           request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
-          request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT));
+          request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
+        snmp_stats.decryptionerrors++;
+        request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
+        request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
+        return ERR_OK;
+      }
     }
 #endif
+    // 9) calculate max size of scoped pdu?
+    // 10) securityname for user is retrieved from usertable?
+    // 11) security data is cached?
+    // 12)
 
     /* Scoped PDU
      * Encryption context
@@ -930,6 +1110,7 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
         &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
     request->context_engine_id_len = (u8_t)u16_value;
+    // TODO: do we need to verify this contextengineid too?
 
     /* contextName */
     IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@@ -940,25 +1121,26 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
         &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
     request->context_name_len = (u8_t)u16_value;
+    // TODO: do we need to verify this contextname too?
   } else
 #endif
   {
-  /* decode community */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    /* decode community */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
 
-  err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
-  if (err == ERR_MEM) {
-    /* community string does not fit in our buffer -> its too long -> its invalid */
-    request->community_strlen = 0;
-    snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
-  } else {
-    IF_PARSE_ASSERT(err == ERR_OK);
-  }
-  /* add zero terminator */
-  request->community[request->community_strlen] = 0;
+    err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+    if (err == ERR_MEM) {
+      /* community string does not fit in our buffer -> its too long -> its invalid */
+      request->community_strlen = 0;
+      snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+    } else {
+      IF_PARSE_ASSERT(err == ERR_OK);
+    }
+    /* add zero terminator */
+    request->community[request->community_strlen] = 0;
   }
 
   /* decode PDU type (next container level) */
@@ -995,6 +1177,7 @@ snmp_parse_inbound_frame(struct snmp_request *request)
       break;
   }
   request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+  request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
 
   /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
   if (request->community_strlen == 0) {
@@ -1244,7 +1427,7 @@ snmp_prepare_outbound_frame(struct snmp_request *request)
 
   /* 'PDU' sequence */
   request->outbound_pdu_offset = pbuf_stream->offset;
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0);
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
   OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
 
   /* request ID */
@@ -1525,7 +1708,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
 #endif
 
   /* complete missing length in 'PDU' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3,
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
       frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
   OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
   OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
diff --git a/src/apps/snmp/snmp_msg.h b/src/apps/snmp/snmp_msg.h
index 2d01ef3..60beaaf 100644
--- a/src/apps/snmp/snmp_msg.h
+++ b/src/apps/snmp/snmp_msg.h
@@ -116,6 +116,9 @@ struct snmp_request
   /* max-repetitions (getBulkRequest (SNMPv2c)) */
   s32_t max_repetitions;
   
+  /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
+  u8_t request_out_type;
+
 #if LWIP_SNMP_V3
   s32_t msg_id;
   s32_t msg_max_size;
@@ -128,7 +131,9 @@ struct snmp_request
   u8_t  msg_user_name[SNMP_V3_MAX_USER_LENGTH];
   u8_t  msg_user_name_len;
   u8_t  msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+  u8_t  msg_authentication_parameters_len;
   u8_t  msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+  u8_t  msg_privacy_parameters_len;
   u8_t  context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
   u8_t  context_engine_id_len;
   u8_t  context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
diff --git a/src/apps/snmp/snmpv3_priv.h b/src/apps/snmp/snmpv3_priv.h
index b87666d..ef8672d 100644
--- a/src/apps/snmp/snmpv3_priv.h
+++ b/src/apps/snmp/snmpv3_priv.h
@@ -51,6 +51,11 @@
 #define SNMP_V3_AUTH_FLAG      0x01
 #define SNMP_V3_PRIV_FLAG      0x02
 
+/* Security levels */
+#define SNMP_V3_NOAUTHNOPRIV   0x00
+#define SNMP_V3_AUTHNOPRIV     SNMP_V3_AUTH_FLAG
+#define SNMP_V3_AUTHPRIV       (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
+
 #define SNMP_V3_MD5_LEN        16
 #define SNMP_V3_SHA_LEN        20
 
diff --git a/src/include/lwip/apps/snmp_core.h b/src/include/lwip/apps/snmp_core.h
index e781c53..e173f50 100644
--- a/src/include/lwip/apps/snmp_core.h
+++ b/src/include/lwip/apps/snmp_core.h
@@ -351,6 +351,14 @@ struct snmp_statistics
   u32_t outsetrequests;
   u32_t outgetresponses;
   u32_t outtraps;
+#if LWIP_SNMP_V3
+  u32_t unsupportedseclevels;
+  u32_t notintimewindows;
+  u32_t unknownusernames;
+  u32_t unknownengineids;
+  u32_t wrongdigests;
+  u32_t decryptionerrors;
+#endif
 };
 
 extern struct snmp_statistics snmp_stats;
-- 
2.8.1.windows.1

