Click here to Skip to main content
15,867,880 members
Articles / Desktop Programming / MFC
Article

How to develop a SNMP extension agent DLL

Rate me:
Please Sign up or sign in to vote.
4.77/5 (65 votes)
11 Dec 20047 min read 346.3K   11.6K   91   75
This covers essentials about SNMP and a short tutorial on developing a very simple SNMP extension agent.

Sample Image - SNMP.jpg

Introduction

Simple Network Management Protocol is the protocol to mange any system over the network. SNMP is a protocol on top of UDP protocol. Like UDP, it also need two ends, i.e., server and client. Should you manage any system over the network, you need a server running on it. In SNMP terms, server is called as Agent. Typically, you can find SNMP agent running on internetworking devices like routers, hubs, switches, bridges and even in some printers. Any PC also can have a SNMP agent running. In this case, you can manage that PC from any where in the world.

Microsoft has designed SNMP agent as a service. This service will do all the dirty work on protocol encoding and decoding. If we need a custom agent, then all we have to do is just develop an extension agent DLL on top of Windows SNMP service. This makes our life much easier. Even in Windows, ignoring this service we can write from scratch. Those who want to design like that can find the SNMP RFC specification on here. SNMP is a huge book; here I'll cover some essentials of SNMP and how to develop an SNMP extension agent DLL for Windows. In the next part, I'll demonstrate how to install SNMP and our custom extension agent DLL on Windows 2000.

SNMP on Windows 2000

Microsoft has implemented SNMP in two services. The first one is SNMP.EXE and the other one is SNMPTRAP.EXE. Microsoft also implemented some extension agent DLL along with the services. Using this, you can get service pack version, get IE patches version, perform DHCP configurations, perform Windows update, etc. You can perform almost every thing on Windows using the default extension agents. So, why you need a custom agent DLL? Say if you want to do custom operation from other end of the world, like back up certain files, retrieve your file from any where, configure your custom build software, etc. Then you must need your own custom extension DLL.

To develop a custom agent, all you got to do is build a DLL with minimum three exported functions. Those are:

  • SnmpExtensionInit
  • SnmpExtensionQuery
  • SnmpExtensionTrap

Along with the SNMP services, Microsoft also provided some APIs to deal SNMP. These APIs cover all your needs to develop an SNMP custom agent. So what are we waiting for? Grab these APIs and pack a DLL.

SNMP Terms

Before starting the development, it is better to know some SNMP terms. RFC standard may threaten you with some terms. But I feel, for beginners, knowledge about the following terms is enough.

MIB: (Management Information Base)

When dealing with remote devices, there should be some standard to understand the information stored in the SNMP agent. SNMP accomplishes that through MIB. Think of the MIB as a "tree" very similar to a Windows, DOS or UNIX directory structure, with some pre-defined values ("directory" names) and some custom areas for vendors. Since this database includes all the manageable objects, MIB is the most basic element of network management. Here is an example of a MIB entry in the tree:

sysUpTime OBJECT-TYPE
  SYNTAX  TimeTicks
  ACCESS  read-only
  STATUS  mandatory
  DESCRIPTION
     "The time (in hundredths of a second) since the 
     network management portion of the system was last re-initialized."
  ::= { system 3 }

OID: (Object Identifier)

OID is the ID to identify the node in the MIB (remember MIB as a tree structure). Don't worry about this too much, you will get to know at the end of this tutorial.

Now if you are new to SNMP, definitely your mind will ask you the following questions..

  • Where this MIB resides?
  • How to access this?
  • Do you need any SQL like query to access this?

Here is the answer to all of that. Even though SNMP standard calls as database, it is not an actual database. Think, it is the way or format or standard to hold the information. Where it resides is up to your decision. If you want, you can put it into registry, or into real Access database, or store it into an ordinary binary file. In this example (MyAgent.DLL), I created MIB by hard coding with a group of C++ structures in global memory (really, it is a structure defined by struct keyword).

Here in this agent, whenever a request comes, I am just going through these structures and fulfilling the request.

Trap:

Traps are like events. This is a way the SNMP agent pushes the information to client. Asynchronously, events can be fired to client from server (agent).

NMS: (Network Management Station)

NMS is nothing but a system where SNMP client (manager) runs. That is where you sit and view the things going on the other end.

Community:

Community is the kind of group you assign the rights and put your agents into. It has nothing to do with extension agent development. It is all in the installation. I shall explain that on the next article.

Now, we'll get our hands dirty

I think you need Platform SDK (I'm not sure). Now, create a Win32 DLL project; define and export the following functions:

BOOL SNMP_FUNC_TYPE SnmpExtensionInit(    DWORD dwUptimeReference,
                    HANDLE *phSubagentTrapEvent,
                    AsnObjectIdentifier *pFirstSupportedRegion )

BOOL SNMP_FUNC_TYPE SnmpExtensionQuery(    BYTE bPduType, 
                    SnmpVarBindList *pVarBindList, 
                    AsnInteger32 *pErrorStatus, 
                    AsnInteger32 *pErrorIndex    )

// you need this only if you need traps from agent
BOOL SNMP_FUNC_TYPE SnmpExtensionTrap(    AsnObjectIdentifier *pEnterpriseOid, 
                    AsnInteger32 *pGenericTrapId, 
                    AsnInteger32 *pSpecificTrapId, 
                    AsnTimeticks *pTimeStamp, 
                    SnmpVarBindList *pVarBindList    )

Then define MIB table (structure) and instances as follows:

// template MIB entry
struct MIB_ENTRY
{
    AsnObjectIdentifier asnOid;
    void *              pStorageValue;
    CHAR *              szStorageName;
    BYTE                chType;
    UINT                unAccess;
    MIB_ENTRY*            pMibNext;
};
// global MIB table instance with three nodes
MIB_ENTRY g_MyMibTable[] = {
    {    
        {OID_SIZEOF(g_unAboutOid),g_unAboutOid},
        &g_szAbout,
        "About",
        ASN_OCTETSTRING,
        SNMP_ACCESS_READ_ONLY,
        &g_MyMibTable[1]
    },
    {
        {OID_SIZEOF(g_unNameOid),g_unNameOid},
        &g_szName,
        "Name",
        ASN_OCTETSTRING,
        SNMP_ACCESS_READ_WRITE,
        &g_MyMibTable[2]
    },
    {
        {OID_SIZEOF(g_unAgeOid),g_unAgeOid},
        &g_asnIntAge,
        "Age",
        ASN_INTEGER,
        SNMP_ACCESS_READ_WRITE,
        NULL
    }
};

Our MIB has three nodes:

  • About: this is a read only node. Gives out the author's name.
  • Name: string variable with read and write access.
  • Age: integer variable with read and write access.

The visualized MIB table could be like this:

Image 2

The next step

Here we should code the functionality by placing some code into the exported functions. Let us peek in to these functions one by one.

Any MIB node value passed in between the server and client should be a structure containing two sub structures. First one to represent object identifier (OID) and the second one to represent that object's associated value. Object's value can be represented in AsnAny structure. Think this structure as similar to VARIANT structure. We should use specific SNMP APIs to manipulate these structures. For example, if you want to copy OID from one variable (AsnObjectIdentifier) to another, you should use SnmpUtilOidCpy.

In the following functions, I have used such SNMP specific APIs. For more detail about these APIs, refer MSDN.

SnmpExtensionInit: This function is called by the service soon after loading our extension agent DLL (MyAgent.DLL). Here, we will create the trap generation thread, create an event for trap, and initialize the MIB table. Also, initialize g_dwStartTime variable even though it is not a must. Most importantly, we will initialize the MIB prefix (pFirstSupportedRegion). Our MIB prefix (i.e., our MIB branch) is .1.3.6.1.4.1.15. If any request comes to this branch, SNMP service will call our DLL's SnmpExtensionQuery exported function.

BOOL SNMP_FUNC_TYPE SnmpExtensionInit(    DWORD dwUptimeReference,
                    HANDLE *phSubagentTrapEvent, 
                    AsnObjectIdentifier *pFirstSupportedRegion)
{
    // creaet this event for the trap
    g_hSimulateTrap = CreateEvent(NULL, FALSE, FALSE, NULL); 

    *pFirstSupportedRegion = MIB_OidPrefix;
    *phSubagentTrapEvent = g_hSimulateTrap; // by assigning it passes to 
                        // the SNMP service
                        // So when ever you set this event 
                        // service will call
                        // SnmpExtensionTrap exported function
    
    // on loading the our SNMP DLL create the thread
    g_hTrapGenThread = CreateThread(NULL,0,TrapGenThread,NULL,0,NULL);

    // hard coded initialization
    g_szAbout = (char*)malloc(sizeof(char)*64);
    strcpy(g_szAbout,"Author : Ramanan.T");
    g_szName = (char*)malloc(sizeof(char)*64);
    strcpy(g_szName,"Your Name");
    g_asnIntAge = 0;

    g_dwStartTime = GetTickCount();

    return SNMPAPI_NOERROR;
}

SnmpExtensionQuery: This query function will be called whenever the client (manager) request comes to our branch. This should support at least three request types. Those are GET, GET NEXT and SET. Our internal custom functions GetRequest, GetNextRequest and SetRequest satisfy these requests. For further details, open the source ZIP and check how these functions extract the information from MIB structures.

BOOL SNMP_FUNC_TYPE SnmpExtensionQuery(    BYTE bPduType, 
                    SnmpVarBindList *pVarBindList, 
                    AsnInteger32 *pErrorStatus, 
                    AsnInteger32 *pErrorIndex)
{
    int nRet = 0;
    AsnObjectName;
    
    *pErrorStatus = SNMP_ERRORSTATUS_NOERROR;
    *pErrorIndex = 0;

    for(UINT i=0;i<pVarBindList->len;i++)
    {
        *pErrorStatus = SNMP_ERRORSTATUS_NOERROR;

        // what type of request we are getting?
        switch(bPduType)
        {
        case SNMP_PDU_GET://gets the variable value 
                    // passed variable in pVarBindList
            *pErrorStatus = GetRequest(&pVarBindList->list[i]);
            if(*pErrorStatus != SNMP_ERRORSTATUS_NOERROR)
                *pErrorIndex++;
            break;
        case SNMP_PDU_GETNEXT: // gets the next variable 
                    // related to the passed variable in pVarBindList
            *pErrorStatus = GetNextRequest(&pVarBindList->list[i]);
            if(*pErrorStatus != SNMP_ERRORSTATUS_NOERROR)
                *pErrorIndex++;
            break;
        case SNMP_PDU_SET: // sets a variable
            *pErrorStatus = SetRequest(&pVarBindList->list[i]);
            if(*pErrorStatus != SNMP_ERRORSTATUS_NOERROR)
                *pErrorIndex++;
            break;
        default:
            *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
            *pErrorIndex++;
        };
    }    

    return SNMPAPI_NOERROR;
}

SnmpExtensionTrap: This function will not generate any traps. This function will be called whenever the event is set (can you remember the event that we initialized in SnmpExtensionInit function?). What this function will do is just fill the parameters passed to it. Using this parameters, SNMPTRAP service will compose the trap containing OID, value, time stamp, etc. and send to all connected clients.

BOOL SNMP_FUNC_TYPE SnmpExtensionTrap(    AsnObjectIdentifier *pEnterpriseOid, 
                    AsnInteger32 *pGenericTrapId, 
                    AsnInteger32 *pSpecificTrapId, 
                    AsnTimeticks *pTimeStamp, 
                    SnmpVarBindList *pVarBindList)
{
    static int nNoOfTraps = 1; // just ignore this, 
                    // I introduced this to 
                    // send many traps at once
                    // any way below we are generating 
                    // one trap with two values

    if(nNoOfTraps) // if it is zero don't send traps
    {
        pEnterpriseOid->idLength = sizeof(g_TrapOid);
        pEnterpriseOid->ids = g_TrapOid;

        *pGenericTrapId        = SNMP_GENERICTRAP_ENTERSPECIFIC;
        *pSpecificTrapId    = 1;   // ToasterControl Up trap.
        *pTimeStamp            = GetTickCount() - g_dwStartTime;

        // Allocate space for the Variable Bindings.
        pVarBindList->list = (SnmpVarBind*)SnmpUtilMemAlloc(2*sizeof(SnmpVarBind));

        SnmpUtilOidCpy(&pVarBindList->list[0].name,&MIB_OidPrefix);
        SnmpUtilOidAppend(&pVarBindList->list[0].name,&g_MyMibTable[1].asnOid);
        pVarBindList->list[0].value.asnType = ASN_OCTETSTRING;
        pVarBindList->list[0].value.asnValue.string.dynamic = TRUE;

        pVarBindList->list[0].value.asnValue.string.length = 
                strlen(*(LPSTR*)g_MyMibTable[1].pStorageValue);

        // oops! code messed up due to alignment problem
        pVarBindList->list[0].value.asnValue.string.stream = 
                (unsigned char*) SnmpUtilMemAlloc (
             pVarBindList->list[0].value.asnValue.string.length * sizeof(char));
    
        memcpy(pVarBindList->list[0].value.asnValue.string.stream,
            *(LPSTR*)g_MyMibTable[1].pStorageValue,pVarBindList->
                list[0].value.asnValue.string.length);
                
        SnmpUtilOidCpy(&pVarBindList->list[1].name,&MIB_OidPrefix);
        SnmpUtilOidAppend(&pVarBindList->list[1].name,&g_MyMibTable[2].asnOid);
        pVarBindList->list[1].value.asnType = ASN_INTEGER;
        pVarBindList->list[1].value.asnValue.number 
                = *((AsnInteger32*)g_MyMibTable[2].pStorageValue);

        pVarBindList->len = 2;

        nNoOfTraps--;
        // Indicate that valid trap data exists in the parameters.
        return TRUE;
    }
    nNoOfTraps = 1;

    // Indicate that no more traps are available
    return FALSE;
}

Come to the point...How can you manage using set and get?

Oh yeh...you have to do all those :). What you can do is code your own function to do some thing when a particular variable is set to a particular value. Here is one suggestion. Say you want to launch a URL in IE. For that, add a node in MIB. Name the variable as launch and make the type as string type. When the client makes the request to set this variable with a URL value, inside the agent, set the variable, and using ShellExecute, launch this URL. Set and Get just setting & getting. According to the variable change, you have to perform the real managing stuff.

Conclusion

Here we go....Now it's your turn to do some very serious SNMP. To make it work, you need to install it properly. I'll explain that on next article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCompiler warning on AsnObjectName; Pin
t1234567890098765432128-Mar-21 19:51
t1234567890098765432128-Mar-21 19:51 
GeneralSending V2 traps Pin
Member 1335335517-Apr-18 0:33
Member 1335335517-Apr-18 0:33 
QuestionCan the snmp extension agent dll be developed in C++/cli? Pin
Kannan Devarajan23-Jul-16 1:21
Kannan Devarajan23-Jul-16 1:21 
QuestionLooking for example code to implement a SNMP table using cpp Pin
yoramdudi26-Oct-15 4:51
yoramdudi26-Oct-15 4:51 
AnswerRe: Looking for example code to implement a SNMP table using cpp Pin
Member 1335335517-Apr-18 0:34
Member 1335335517-Apr-18 0:34 
Bugviolation of SnmpExtensionQuery requirements Pin
ThomasNY2-Feb-15 1:38
ThomasNY2-Feb-15 1:38 
QuestionDoes the example DLL work on 64bit systems? Pin
Member 113173205-Jan-15 5:53
Member 113173205-Jan-15 5:53 
AnswerRe: Does the example DLL work on 64bit systems? Pin
Member 113173207-Jan-15 23:51
Member 113173207-Jan-15 23:51 
GeneralRe: Does the example DLL work on 64bit systems? Pin
Member 118061737-Jul-15 2:45
Member 118061737-Jul-15 2:45 
BugSmall bug in code (line 180 of MyAgent.cpp) Pin
EDDY7447-Nov-14 23:02
EDDY7447-Nov-14 23:02 
QuestionCan I translate it into Chinese? Pin
coperator15-Nov-13 18:59
coperator15-Nov-13 18:59 
QuestionMIB file to MyAgent.dll Pin
dafna_sahar29-Sep-13 2:55
dafna_sahar29-Sep-13 2:55 
QuestionHow to register the extention snmp dll? Pin
dafna_sahar29-Sep-13 2:34
dafna_sahar29-Sep-13 2:34 
QuestionNyc and helpful work Ramanan. I am having a question regarding MIB. Pin
Member 951813217-Oct-12 19:21
Member 951813217-Oct-12 19:21 
QuestionZIP files are broken Pin
Member 31699338-May-12 22:29
Member 31699338-May-12 22:29 
QuestionNetwork Elements Pin
ShaminaMina18-Apr-12 3:28
ShaminaMina18-Apr-12 3:28 
QuestionRestart SNMP service Pin
Dansveen17-Mar-12 4:50
Dansveen17-Mar-12 4:50 
Questionmib tree with subtrees and sequence structure Pin
jingxieee16-Jan-12 16:19
jingxieee16-Jan-12 16:19 
QuestionDoes this work on Vista or Windows 7 ?? Pin
Oliver Aldridge25-Oct-11 7:53
Oliver Aldridge25-Oct-11 7:53 
AnswerRe: Does this work on Vista or Windows 7 ?? Pin
BackToBasic8-Nov-11 3:51
BackToBasic8-Nov-11 3:51 
QuestionThreads count work with SNMP extansion agent Pin
Gendolph5-Sep-11 4:51
Gendolph5-Sep-11 4:51 
QuestionAllocated memory in SnmpExtensionTrap Pin
BackToBasic10-Jul-11 22:51
BackToBasic10-Jul-11 22:51 
AnswerRe: Allocated memory in SnmpExtensionTrap Pin
evoisard13-Nov-12 10:38
evoisard13-Nov-12 10:38 
GeneralMy vote of 5 Pin
rrossenbg10-Jun-11 13:55
rrossenbg10-Jun-11 13:55 
GeneralNice Work, Tks! Pin
IicFox10-Jan-11 14:18
IicFox10-Jan-11 14:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.