diff --git a/src/apps/snmp/snmp_core_priv.h b/src/apps/snmp/snmp_core_priv.h index 5552177d..51704fcc 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 7e36f299..c194ae5e 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,40 +914,12 @@ 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)); - -#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 }; - u8_t key[20]; - u8_t algo; - u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)]; - struct snmp_pbuf_stream auth_stream; - - /* 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)); - /* Set auth parameters to zero for verification */ - IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_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 + request->msg_authentication_parameters_len = (u8_t)u16_value; /* msgPrivacyParameters */ memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH); @@ -894,10 +930,145 @@ snmp_parse_inbound_frame(struct snmp_request *request) 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 }; + u8_t key[20]; + u8_t algo; + 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(&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(&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)); + + 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; + } + + // 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 2d01ef36..60beaafe 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 b87666da..ef8672da 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 e781c532..e173f507 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;