Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/snmp/snmp_agent.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file snmp_agent.c
 * @brief SNMP agent (Simple Network Management Protocol)
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @section Description
 *
 * SNMP is a simple protocol by which management information for a network
 * element may be inspected or altered by logically remote users. Refer
 * to the following RFCs for complete details:
 * - RFC 1157: A Simple Network Management Protocol (SNMP)
 * - RFC 1905: Protocol Operations for Version 2 of the Simple Network
 *     Management Protocol (SNMPv2)
 * - RFC 3410: Introduction and Applicability Statements for Internet
 *     Standard Management Framework
 * - RFC 3411: An Architecture for Describing SNMP Management Frameworks
 * - RFC 3412: Message Processing and Dispatching for the SNMP
 * - RFC 3413: Simple Network Management Protocol (SNMP) Applications
 * - RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of
 *     SNMP Framework
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL SNMP_TRACE_LEVEL

//Dependencies
#include "core/net.h"
#include "snmp/snmp_agent.h"
#include "snmp/snmp_agent_dispatch.h"
#include "snmp/snmp_agent_pdu.h"
#include "snmp/snmp_agent_misc.h"
#include "mibs/mib2_module.h"
#include "crypto.h"
#include "asn1.h"
#include "oid.h"
#include "debug.h"

//Check TCP/IP stack configuration
#if (SNMP_AGENT_SUPPORT == ENABLED)


/**
 * @brief Initialize settings with default values
 * @param[out] settings Structure that contains SNMP agent settings
 **/

void snmpAgentGetDefaultSettings(SnmpAgentSettings *settings)
{
   //The SNMP agent is not bound to any interface
   settings->interface = NULL;

   //Minimum version accepted by the SNMP agent
   settings->versionMin = SNMP_VERSION_1;
   //Maximum version accepted by the SNMP agent
   settings->versionMax = SNMP_VERSION_3;

   //SNMP port number
   settings->port = SNMP_PORT;
   //SNMP trap port number
   settings->trapPort = SNMP_TRAP_PORT;

   //Random data generation callback function
   settings->randCallback = NULL;
}


/**
 * @brief SNMP agent initialization
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] settings SNMP agent specific settings
 * @return Error code
 **/

error_t snmpAgentInit(SnmpAgentContext *context, const SnmpAgentSettings *settings)
{
   error_t error;

   //Debug message
   TRACE_INFO("Initializing SNMP agent...\r\n");

   //Ensure the parameters are valid
   if(context == NULL || settings == NULL)
      return ERROR_INVALID_PARAMETER;

   //Check minimum and maximum SNMP versions
   if(settings->versionMin > settings->versionMax)
      return ERROR_INVALID_PARAMETER;

   //Clear the SNMP agent context
   memset(context, 0, sizeof(SnmpAgentContext));

   //Save user settings
   context->settings = *settings;

#if (SNMP_V3_SUPPORT == ENABLED)
   //Get current time
   context->systemTime = osGetSystemTime();

   //Each SNMP engine maintains two values, snmpEngineBoots and snmpEngineTime,
   //which taken together provide an indication of time at that SNMP engine
   context->engineBoots = 1;
   context->engineTime = 0;

   //Check whether SNMPv3 is supported
   if(settings->versionMin <= SNMP_VERSION_3 &&
      settings->versionMax >= SNMP_VERSION_3)
   {
      //Make sure a random number generator has been registered
      if(settings->randCallback == NULL)
         return ERROR_INVALID_PARAMETER;

      //The salt integer is initialized to an arbitrary value at boot time
      error = settings->randCallback((uint8_t *) &context->salt, sizeof(context->salt));
      //Any error to report?
      if(error)
         return error;
   }
#endif

   //Create a mutex to prevent simultaneous access to SNMP agent context
   if(!osCreateMutex(&context->mutex))
   {
      //Failed to create mutex
      return ERROR_OUT_OF_RESOURCES;
   }

   //Open a UDP socket
   context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);

   //Failed to open socket?
   if(!context->socket)
   {
      //Clean up side effects
      osDeleteMutex(&context->mutex);
      //Report an error
      return ERROR_OPEN_FAILED;
   }

   //Start of exception handling block
   do
   {
      //Explicitly associate the socket with the relevant interface
      error = socketBindToInterface(context->socket, settings->interface);
      //Unable to bind the socket to the desired interface?
      if(error)
         break;

      //The SNMP agent listens for messages on port 161
      error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
      //Unable to bind the socket to the desired port?
      if(error)
         break;

      //End of exception handling block
   } while(0);

   //Any error to report?
   if(error)
   {
      //Clean up side effects
      osDeleteMutex(&context->mutex);
      //Close underlying socket
      socketClose(context->socket);
   }

   //Return status code
   return error;
}


/**
 * @brief Start SNMP agent
 * @param[in] context Pointer to the SNMP agent context
 * @return Error code
 **/

error_t snmpAgentStart(SnmpAgentContext *context)
{
   OsTask *task;

   //Debug message
   TRACE_INFO("Starting SNMP agent...\r\n");

   //Make sure the SNMP agent context is valid
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Start the SNMP agent service
   task = osCreateTask("SNMP Agent", (OsTaskCode) snmpAgentTask,
      context, SNMP_AGENT_STACK_SIZE, SNMP_AGENT_PRIORITY);

   //Unable to create the task?
   if(task == OS_INVALID_HANDLE)
      return ERROR_OUT_OF_RESOURCES;

   //The SNMP agent has successfully started
   return NO_ERROR;
}


/**
 * @brief Load a MIB module
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] module Pointer the MIB module to be loaded
 * @return Error code
 **/

error_t snmpAgentLoadMib(SnmpAgentContext *context, const MibModule *module)
{
   error_t error;
   uint_t i;
   uint_t j;

   //Check parameters
   if(context == NULL || module == NULL)
      return ERROR_INVALID_PARAMETER;
   if(module->numObjects < 1)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Loop through existing MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Check whether the specified MIB module is already loaded
      if(context->mibModule[i] == module)
         break;
   }

   //MIB module found?
   if(i < context->mibModuleCount)
   {
      //Prevent the SNMP agent from loading the specified MIB multiple times
      error = NO_ERROR;
   }
   else
   {
      //Make sure there is enough room to add the specified MIB
      if(context->mibModuleCount < SNMP_AGENT_MAX_MIB_COUNT)
      {
         //Loop through existing MIBs
         for(i = 0; i < context->mibModuleCount; i++)
         {
            //Compare object identifiers
            if(oidComp(module->objects[0].oid, module->objects[0].oidLen,
               context->mibModule[i]->objects[0].oid, context->mibModule[i]->objects[0].oidLen) < 0)
            {
               //Make room for the new MIB
               for(j = context->mibModuleCount; j > i; j--)
                  context->mibModule[j] = context->mibModule[j - 1];

               //We are done
               break;
            }
         }

         //Insert the new MIB to the list
         context->mibModule[i] = module;
         //Update the number of MIBs
         context->mibModuleCount++;

         //Successful processing
         error = NO_ERROR;
      }
      else
      {
         //Failed to load the specified MIB
         error = ERROR_OUT_OF_RESOURCES;
      }
   }

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Return status code
   return error;
}


/**
 * @brief Unload a MIB module
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] module Pointer the MIB module to be unloaded
 * @return Error code
 **/

error_t snmpAgentUnloadMib(SnmpAgentContext *context, const MibModule *module)
{
   error_t error;
   uint_t i;
   uint_t j;

   //Check parameters
   if(context == NULL || module == NULL)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Loop through existing MIBs
   for(i = 0; i < context->mibModuleCount; i++)
   {
      //Check whether the specified MIB module is already loaded
      if(context->mibModule[i] == module)
         break;
   }

   //MIB module found?
   if(i < context->mibModuleCount)
   {
      //Update the number of MIBs
      context->mibModuleCount--;

      //Remove the specified MIB from the list
      for(j = i; j < context->mibModuleCount; j++)
         context->mibModule[j] = context->mibModule[j + 1];

      //Successful processing
      error = NO_ERROR;
   }
   else
   {
      //Failed to unload the specified MIB
      error = ERROR_NOT_FOUND;
   }

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Return status code
   return error;
}


/**
 * @brief Set the value of the snmpEngineBoots variable
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] engineBoots Number of times the SNMP engine has re-booted
 * @return Error code
 **/

error_t snmpAgentSetEngineBoots(SnmpAgentContext *context, int32_t engineBoots)
{
#if (SNMP_V3_SUPPORT == ENABLED)
   //Check parameters
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   if(engineBoots < 0)
      return ERROR_OUT_OF_RANGE;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Get current time
   context->systemTime = osGetSystemTime();

   //Set the value of the snmpEngineBoots
   context->engineBoots = engineBoots;
   //The snmpEngineTime is reset to zero
   context->engineTime = 0;

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Successful processing
   return NO_ERROR;
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Get the value of the snmpEngineBoots variable
 * @param[in] context Pointer to the SNMP agent context
 * @param[out] engineBoots Number of times the SNMP engine has re-booted
 * @return Error code
 **/

error_t snmpAgentGetEngineBoots(SnmpAgentContext *context, int32_t *engineBoots)
{
#if (SNMP_V3_SUPPORT == ENABLED)
   //Check parameters
   if(context == NULL || engineBoots == NULL)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);
   //Get the current value of the snmpEngineBoots
   *engineBoots = context->engineBoots;
   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Successful processing
   return NO_ERROR;
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Set enterprise OID
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] enterpriseOid Pointer to the enterprise OID
 * @param[in] enterpriseOidLen Length of the enterprise OID
 * @return Error code
 **/

error_t snmpAgentSetEnterpriseOid(SnmpAgentContext *context,
   const uint8_t *enterpriseOid, size_t enterpriseOidLen)
{
   //Check parameters
   if(context == NULL || enterpriseOid == NULL)
      return ERROR_INVALID_PARAMETER;
   if(enterpriseOidLen > SNMP_MAX_OID_SIZE)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Set enterprise OID
   memcpy(context->enterpriseOid, enterpriseOid, enterpriseOidLen);
   //Save the length of the enterprise OID
   context->enterpriseOidLen = enterpriseOidLen;

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set context engine identifier
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] contextEngine Pointer to the context engine identifier
 * @param[in] contextEngineLen Length of the context engine identifier
 * @return Error code
 **/

error_t snmpAgentSetContextEngine(SnmpAgentContext *context,
   const void *contextEngine, size_t contextEngineLen)
{
#if (SNMP_V3_SUPPORT == ENABLED)
   //Check parameters
   if(context == NULL || contextEngine == NULL)
      return ERROR_INVALID_PARAMETER;
   if(contextEngineLen > SNMP_MAX_CONTEXT_ENGINE_SIZE)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Set context engine identifier
   memcpy(context->contextEngine, contextEngine, contextEngineLen);
   //Save the length of the context engine identifier
   context->contextEngineLen = contextEngineLen;

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Successful processing
   return NO_ERROR;
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Set context name
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] contextName NULL-terminated string that contains the context name
 * @return Error code
 **/

error_t snmpAgentSetContextName(SnmpAgentContext *context,
   const char_t *contextName)
{
#if (SNMP_V3_SUPPORT == ENABLED)
   size_t n;

   //Check parameters
   if(context == NULL || contextName == NULL)
      return ERROR_INVALID_PARAMETER;

   //Retrieve the length of the context name
   n = strlen(contextName);

   //Make sure the context name is valid
   if(n > SNMP_MAX_CONTEXT_NAME_LEN)
      return ERROR_INVALID_LENGTH;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);
   //Set context name
   strcpy(context->contextName, contextName);
   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Successful processing
   return NO_ERROR;
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Create a new community string
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] community NULL-terminated string that contains the community name
 * @param[in] mode Access rights
 * @return Error code
 **/

error_t snmpAgentCreateCommunity(SnmpAgentContext *context,
   const char_t *community, SnmpAccess mode)
{
#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
   //Add the community string to the local configuration datastore
   return snmpAgentCreateUser(context, community, mode, SNMP_KEY_FORMAT_NONE,
      SNMP_AUTH_PROTOCOL_NONE, NULL, SNMP_PRIV_PROTOCOL_NONE, NULL);
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Remove a community string
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] community NULL-terminated string that contains the community name
 * @return Error code
 **/

error_t snmpAgentDeleteCommunity(SnmpAgentContext *context, const char_t *community)
{
#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
   //Remove the community string from the local configuration datastore
   return snmpAgentDeleteUser(context, community);
#else
   //Not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Create a new user
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] username NULL-terminated string that contains the user name
 * @param[in] mode Access rights
 * @param[in] keyFormat Key format (ASCII password or raw key)
 * @param[in] authProtocol Authentication type
 * @param[in] authKey Key to be used for data authentication
 * @param[in] privProtocol Privacy type
 * @param[in] privKey Key to be used for data encryption
 * @return Error code
 **/

error_t snmpAgentCreateUser(SnmpAgentContext *context,
   const char_t *username, SnmpAccess mode, SnmpKeyFormat keyFormat,
   SnmpAuthProtocol authProtocol, const void *authKey,
   SnmpPrivProtocol privProtocol, const void *privKey)
{
   error_t error;
   uint_t i;
   size_t n;
   SnmpUserInfo *entry;
   SnmpUserInfo *firstFreeEntry;

   //Check parameters
   if(context == NULL || username == NULL)
      return ERROR_INVALID_PARAMETER;

   //Data authentication?
   if(authProtocol != SNMP_AUTH_PROTOCOL_NONE)
   {
      //Check key format
      if(keyFormat != SNMP_KEY_FORMAT_TEXT && keyFormat != SNMP_KEY_FORMAT_RAW)
         return ERROR_INVALID_PARAMETER;

      //Data authentication requires a key
      if(authKey == NULL)
         return ERROR_INVALID_PARAMETER;
   }

   //Data confidentiality?
   if(privProtocol != SNMP_PRIV_PROTOCOL_NONE)
   {
      //Check key format
      if(keyFormat != SNMP_KEY_FORMAT_TEXT && keyFormat != SNMP_KEY_FORMAT_RAW)
         return ERROR_INVALID_PARAMETER;

      //Data confidentiality requires a key
      if(privKey == NULL)
         return ERROR_INVALID_PARAMETER;

      //There is no provision for data confidentiality without data authentication
      if(authProtocol == SNMP_AUTH_PROTOCOL_NONE)
         return ERROR_INVALID_PARAMETER;
   }

   //Retrieve the length of the user name
   n = strlen(username);

   //Make sure the user name is valid
   if(n == 0 || n > SNMP_MAX_USER_NAME_LEN)
      return ERROR_INVALID_LENGTH;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Keep track of the first free entry
   firstFreeEntry = NULL;

   //Loop through the list of users
   for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
   {
      //Point to the current entry
      entry = &context->userTable[i];

      //Check if the entry is currently in use
      if(entry->name[0] != '\0')
      {
         //Check whether the user name already exists
         if(!strcmp(entry->name, username))
            break;
      }
      else
      {
         //Keep track of the first free entry
         if(firstFreeEntry == NULL)
            firstFreeEntry = entry;
      }
   }

   //If the specified user name does not exist, then a new
   //entry should be created
   if(i >= SNMP_AGENT_MAX_USER_COUNT)
      entry = firstFreeEntry;

   //Check whether the service list runs out of space
   if(entry != NULL)
   {
      //Save user name
      strcpy(entry->name, username);
      //Access rights
      entry->mode = mode;

      //Successful processing
      error = NO_ERROR;

#if (SNMP_V3_SUPPORT == ENABLED)
      //Authentication protocol
      entry->authProtocol = authProtocol;
      //Privacy protocol
      entry->privProtocol = privProtocol;

      //Data authentication?
      if(authProtocol != SNMP_AUTH_PROTOCOL_NONE)
      {
         //ASCII password or raw key?
         if(keyFormat == SNMP_KEY_FORMAT_TEXT)
         {
            //Generate the authentication key from the provided password
            error = snmpGenerateKey(authProtocol, authKey, context->contextEngine,
               context->contextEngineLen, &entry->authKey);
         }
         else
         {
            //Save the authentication key
            memcpy(&entry->authKey, authKey, sizeof(SnmpKey));
         }
      }

      //Check status code
      if(!error)
      {
         //Data confidentiality?
         if(privProtocol != SNMP_PRIV_PROTOCOL_NONE)
         {
            //ASCII password or raw key?
            if(keyFormat == SNMP_KEY_FORMAT_TEXT)
            {
               //Generate the privacy key from the provided password
               error = snmpGenerateKey(authProtocol, privKey, context->contextEngine,
                  context->contextEngineLen, &entry->privKey);
            }
            else
            {
               //Save the privacy key
               memcpy(&entry->privKey, privKey, sizeof(SnmpKey));
            }
         }
      }

      //Check status code
      if(error)
      {
         //Clean up side effects
         memset(entry, 0, sizeof(SnmpUserInfo));
      }
#endif
   }
   else
   {
      //Unable to add new user
      error = ERROR_OUT_OF_RESOURCES;
   }

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Return error code
   return error;
}


/**
 * @brief Remove existing user
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] username NULL-terminated string that contains the user name
 * @return Error code
 **/

error_t snmpAgentDeleteUser(SnmpAgentContext *context, const char_t *username)
{
   error_t error;
   uint_t i;
   SnmpUserInfo *entry;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Loop through the list of users
   for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
   {
      //Point to the current entry
      entry = &context->userTable[i];

      //Compare user names
      if(!strcmp(entry->name, username))
         break;
   }

   //User name found?
   if(i < SNMP_AGENT_MAX_USER_COUNT)
   {
      //Clear the security profile of the user
      memset(entry, 0, sizeof(SnmpUserInfo));
      //Successful processing
      error = NO_ERROR;
   }
   else
   {
      //The specified user name does not exist
      error = ERROR_NOT_FOUND;
   }

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Return status code
   return error;
}


/**
 * @brief Send SNMP trap message
 * @param[in] context Pointer to the SNMP agent context
 * @param[in] destIpAddr Destination IP address
 * @param[in] version SNMP version identifier
 * @param[in] username User name or community name
 * @param[in] genericTrapType Generic trap type
 * @param[in] specificTrapCode Specific code
 * @param[in] objectList List of object names
 * @param[in] objectListSize Number of entries in the list
 * @return Error code
 **/

error_t snmpAgentSendTrap(SnmpAgentContext *context, const IpAddr *destIpAddr,
   SnmpVersion version, const char_t *username, uint_t genericTrapType,
   uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize)
{
   error_t error;

   //Check parameters
   if(context == NULL || destIpAddr == NULL || username == NULL)
      return ERROR_INVALID_PARAMETER;

   //Make sure the list of objects is valid
   if(objectListSize > 0 && objectList == NULL)
      return ERROR_INVALID_PARAMETER;

   //Acquire exclusive access to the SNMP agent context
   osAcquireMutex(&context->mutex);

   //Refresh SNMP engine time
   snmpRefreshEngineTime(context);

   //Start of exception handling block
   do
   {
#if (SNMP_V1_SUPPORT == ENABLED)
      //SNMPv1 version?
      if(version == SNMP_VERSION_1)
      {
         //Format Trap-PDU
         error = snmpFormatTrapPdu(context, version, username,
            genericTrapType, specificTrapCode, objectList, objectListSize);
         //Any error to report?
         if(error)
            break;

         //Format SMNP message header
         error = snmpWriteMessageHeader(&context->response);
         //Any error to report?
         if(error)
            break;
      }
      else
#endif
#if (SNMP_V2C_SUPPORT == ENABLED)
      //SNMPv2c version?
      if(version == SNMP_VERSION_2C)
      {
         //Format SNMPv2-Trap-PDU
         error = snmpFormatTrapPdu(context, version, username,
            genericTrapType, specificTrapCode, objectList, objectListSize);
         //Any error to report?
         if(error)
            break;

         //Format SMNP message header
         error = snmpWriteMessageHeader(&context->response);
         //Any error to report?
         if(error)
            break;
      }
      else
#endif
#if (SNMP_V3_SUPPORT == ENABLED)
      //SNMPv3 version?
      if(version == SNMP_VERSION_3)
      {
         //Information about the user name is extracted from the local
         //configuration datastore
         context->user = snmpFindUser(context, username, strlen(username));

         //Invalid user name?
         if(context->user == NULL)
         {
            //Report an error
            error = ERROR_UNKNOWN_USER_NAME;
            //Exit immediately
            break;
         }

         //Format SNMPv2-Trap-PDU
         error = snmpFormatTrapPdu(context, version, username,
            genericTrapType, specificTrapCode, objectList, objectListSize);
         //Any error to report?
         if(error)
            break;

         //Format scopedPDU
         error = snmpWriteScopedPdu(&context->response);
         //Any error to report?
         if(error)
            break;

         //Check whether the privFlag is set
         if(context->response.msgFlags & SNMP_MSG_FLAG_PRIV)
         {
            //Encrypt data
            error = snmpEncryptData(context->user, &context->response, &context->salt);
            //Any error to report?
            if(error)
               break;
         }

         //Format SMNP message header
         error = snmpWriteMessageHeader(&context->response);
         //Any error to report?
         if(error)
            break;

         //Check whether the authFlag is set
         if(context->response.msgFlags & SNMP_MSG_FLAG_AUTH)
         {
            //Authenticate outgoing SNMP message
            error = snmpAuthOutgoingMessage(context->user, &context->response);
            //Any error to report?
            if(error)
               break;
         }
      }
      else
#endif
      //Invalid SNMP version?
      {
         //Debug message
         TRACE_WARNING("  Invalid SNMP version!\r\n");
         //Report an error
         error = ERROR_INVALID_VERSION;
         //Exit immediately
         break;
      }

      //Total number of messages which were passed from the SNMP protocol
      //entity to the transport service
      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutPkts, 1);

      //Debug message
      TRACE_INFO("Sending SNMP message to %s port %" PRIu16
         " (%" PRIuSIZE " bytes)...\r\n",
         ipAddrToString(destIpAddr, NULL),
         context->settings.trapPort, context->response.length);

      //Display the contents of the SNMP message
      TRACE_DEBUG_ARRAY("  ", context->response.pos, context->response.length);
      //Display ASN.1 structure
      asn1DumpObject(context->response.pos, context->response.length, 0);

      //Send SNMP trap message
      error = socketSendTo(context->socket, destIpAddr, context->settings.trapPort,
         context->response.pos, context->response.length, NULL, 0);

      //End of exception handling block
   } while(0);

   //Release exclusive access to the SNMP agent context
   osReleaseMutex(&context->mutex);

   //Return status code
   return error;
}


/**
 * @brief SNMP agent task
 * @param[in] context Pointer to the SNMP agent context
 **/

void snmpAgentTask(SnmpAgentContext *context)
{
   error_t error;

#if (NET_RTOS_SUPPORT == ENABLED)
   //Main loop
   while(1)
   {
#endif
      //Wait for an incoming datagram
      error = socketReceiveFrom(context->socket, &context->remoteIpAddr,
         &context->remotePort, context->request.buffer,
         SNMP_MAX_MSG_SIZE, &context->request.bufferLen, 0);

      //Any datagram received?
      if(!error)
      {
         //Acquire exclusive access to the SNMP agent context
         osAcquireMutex(&context->mutex);

         //Debug message
         TRACE_INFO("\r\nSNMP message received from %s port %" PRIu16
            " (%" PRIuSIZE " bytes)...\r\n",
            ipAddrToString(&context->remoteIpAddr, NULL),
            context->remotePort, context->request.bufferLen);

         //Display the contents of the SNMP message
         TRACE_DEBUG_ARRAY("  ", context->request.buffer, context->request.bufferLen);
         //Dump ASN.1 structure
         asn1DumpObject(context->request.buffer, context->request.bufferLen, 0);

         //Process incoming SNMP message
         error = snmpProcessMessage(context);

         //Check status code
         if(!error)
         {
            //Debug message
            TRACE_INFO("Sending SNMP message to %s port %" PRIu16
               " (%" PRIuSIZE " bytes)...\r\n",
               ipAddrToString(&context->remoteIpAddr, NULL),
               context->remotePort, context->response.length);

            //Display the contents of the SNMP message
            TRACE_DEBUG_ARRAY("  ", context->response.pos, context->response.length);
            //Display ASN.1 structure
            asn1DumpObject(context->response.pos, context->response.length, 0);

            //Send SNMP response message
            socketSendTo(context->socket, &context->remoteIpAddr, context->remotePort,
               context->response.pos, context->response.length, NULL, 0);
         }

         //Release exclusive access to the SNMP agent context
         osReleaseMutex(&context->mutex);
      }
#if (NET_RTOS_SUPPORT == ENABLED)
   }
#endif
}

#endif