/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* tcpip720 src/tcpip/usr/samples/snmpd/dpi2/dpi_sample.c 1.2 */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* Restricted Materials of IBM */ /* */ /* COPYRIGHT International Business Machines Corp. 1996,1998 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ static char sccsid[] = "@(#)38 1.2 src/tcpip/usr/samples/snmpd/dpi2/dpi_sample.c, snmp, tcpip720 2/17/98 17:12:31"; /* \begin{verbatim} */ /* ------------------------------------------------------------------* * $Id:$ * ------------------------------------------------------------------*/ /*********************************************************************/ /* */ /* SNMP-DPI API - SNMP Distributed Protocol Interface */ /* Application Programming Interface */ /* */ /* May 15, 1997 - Version 2.0.25 */ /* */ /* Copyright - (C) International Business Machines Corp. 1994-1997*/ /* */ /* Permission to use, copy, modify, and distribute this software */ /* and its documentation for any lawful purpose and without fee is */ /* hereby granted, provided that this notice be retained unaltered,*/ /* and that the names of IBM and all other contributors shall not */ /* be used in advertising or publicity pertaining to distribution */ /* of the software without specific written prior permission. */ /* No contributor makes any representations about the suitability */ /* of this software for any purpose. It is provided "as is" */ /* without express or implied warranty. */ /* */ /* IBM AND ALL OTHER CONTRIBUTORS DISCLAIM ALL WARRANTIES WITH */ /* REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF */ /* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, */ /* AND NON-INFRINGEMENT. */ /* */ /* IN NO EVENT SHALL IBM OR ANY OTHER CONTRIBUTOR BE LIABLE FOR */ /* ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN */ /* CONTRACT, TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN */ /* CONNECTION WITH, THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* */ /* dpi_sample.c - a simple sample DPI subagent to show how the SNMP */ /* DPI API can be used to implement a public or */ /* private MIB. */ /* */ /* Notes: */ /* 1. This sample does not implement a real MIB but just uses an */ /* arbitrary subtree as a sample. We have named it dpiSimpleMIB */ /* and there is also a MIB writeup useable by the SMIC compiler. */ /* 2. The code is meant to show in each segment of the code how to */ /* use the incoming DPI packet and how to create a DPI response */ /* packet. It does NOT do the proper checking on the data. */ /* 3. Nevertheless, the code shows enough of the DPI API so that a */ /* programmer can see how the DPI API can be used. */ /* 4. The code does do proper searching for the next lexicographical */ /* OID for a GETNEXT. This is because in GETNEXT it is easy to */ /* make errors, and we try to show how to do it. However, we do */ /* not show how to do it in tables which may be indexed in quite */ /* a few different ways. This is something to add in the future. */ /* For time being, the DPI sub-agent programmer must try to do it */ /* without a sample. */ /*********************************************************************/ /* $Log:$ *-------------------------------------------------------------------*/ #define VERSION \ "2.0.25, May 15 1997, Compiled "__DATE__" at "__TIME__ #include #include #include #include #include /* DPI 2.0 API definitions */ #ifdef DPI_VERY_MINIMAL_SUBAGENT /* with VERY minimal agent */ #define DPIconnect_to_agent_SHM(a) -1; /* there is no support for */ #define DPIdebug(a) ; /* these functions */ #endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */ static int handle; /* handle has global scope */ /*********************************************************************/ /* Our OBJECT IDENTIFIERS as defined in the dpiSimpleMIB */ /* Your SUBAGENT OID could be different. */ /* We are using COUNTER64 in this sample, but please realize that */ /* this is a SNMPv2 SMI-type, and so not all agents and managers */ /* may support this type yet. You may want to remove it if you are */ /* working in an SNMPv1 world. */ /*********************************************************************/ #define DPI_SIMPLE_SUBAGENT "1.3.6.1.4.1.2.2.1.5" #define DPI_SIMPLE_MIB "1.3.6.1.4.1.2.2.1.5." #define DPI_SIMPLE_INTEGER "1.0" /* dpiSimpleInteger.0 */ #define DPI_SIMPLE_STRING "2.0" /* dpiSimpleString.0 */ #define DPI_SIMPLE_COUNTER32 "3.0" /* dpiSimpleCounter32.0 */ #define DPI_SIMPLE_COUNTER64 "4.0" /* dpiSimpleCounter64.0 */ #define IP_LOOPBACK "localhost" /* Common IP loopback address */ #define SNMP_COMMUNITY "public" /* Commonly used public community */ /*********************************************************************/ /* these are our 4 variables dpiSimpleInteger, dpiSimpleString and */ /* dpiSimpleCounter32 and dpiSimpleCounter64. */ /* We use them global throughout this file. */ /* For value2 (writeable) we need ptrs to save new and old values, */ /* so that we can properly do SET/COMMIT/UNDO sequences. */ /*********************************************************************/ static long int value1 = 5; #define value2_p cur_val_p /* writable object */ #define value2_len cur_val_len /* writable object */ static char *cur_val_p = (char *)0; static char *new_val_p = (char *)0; static char *old_val_p = (char *)0; static unsigned long cur_val_len = 0; static unsigned long new_val_len = 0; static unsigned long old_val_len = 0; static unsigned long value3 = 1; #ifndef EXCLUDE_SNMP_V2_SUPPORT static snmp_dpi_u64 value4 = {0x80000000,1L}; #endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */ static int shared_mem = 0; /* default use TCP */ static unsigned short timeout = 3; /* default timeout */ static void do_connect_and_open(char *hostname_p, char *community_p); static void do_register(void); static int do_trap(void); static int do_get(snmp_dpi_hdr *hdr_p, snmp_dpi_get_packet *pack_p); static int do_set(snmp_dpi_hdr *hdr_p, snmp_dpi_set_packet *pack_p); static int do_next(snmp_dpi_hdr *hdr_p, snmp_dpi_next_packet *pack_p); static int do_unreg(snmp_dpi_hdr *hdr_p,snmp_dpi_ureg_packet *pack_p); static int do_close(snmp_dpi_hdr *hdr_p,snmp_dpi_close_packet *pack_p); #ifndef DPI_VERY_MINIMAL_SUBAGENT /* with VERY minimal agent */ static void usage(char *cmd_p) { printf("\nUsage: "); printf("%s [-d [level]] [-h hostname] [-c community] [-shm]\n",cmd_p); printf("\nWhere: -d level - debug level, default level=1\n"); printf(" -h host - send request to specified host\n"); printf(" -c community - use specified community name\n"); printf(" -shm - connect over shared memory, not TCP\n"); printf("\nDefaults: %s -d 0 -h %s -c %s\n", cmd_p, IP_LOOPBACK, SNMP_COMMUNITY); printf("\n%s - %s\n",__FILE__, VERSION); exit(1); } #else #define usage(a) exit(1) #endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */ /*********************************************************************/ /* main line function */ /*********************************************************************/ /* */ /* First we call a function to open up a connection to the agent and */ /* to identify ourselves to the agent. That function sets the global */ /* variable "handle" which represents our connection to the agent. */ /* */ /* Second we call a function to register our sample MIB subtree. */ /* */ /* After we have registered our sample MIB subtree with the agent, */ /* we must expect that SNMP requests for that subtree will be passed */ /* for processing by the us. The requests will arrive in the form of */ /* DPI packets on the connection that the we have established. So we */ /* go into a while loop to await DPI packets from the agent. */ /* */ /* The subagent cannot know in advance which kind of packet arrives */ /* from the agent. So we await a DPI packet (forever), then we parse */ /* the packet, check the packet type and process the request based */ /* on the DPI packet type. A call to pDPIpacket (which expects as */ /* argument a ptr to the encoded/serialized DPI packet) returns us */ /* a ptr to a DPI parse tree. The ptr points to a snmp_dpi_hdr */ /* structure which looks as follows: */ /* */ /* struct snmp_dpi_hdr { */ /* unsigned char proto_major; */ /* unsigned char proto_version; */ /* unsigned char proto_release; */ /* unsigned short packet_id; */ /* unsigned char packet_type; */ /* union { */ /* snmp_dpi_reg_packet *reg_p; */ /* snmp_dpi_ureg_packet *ureg_p; */ /* snmp_dpi_get_packet *get_p; */ /* snmp_dpi_next_packet *next_p; */ /* snmp_dpi_bulk_packet *bulk_p; */ /* snmp_dpi_set_packet *set_p; */ /* snmp_dpi_resp_packet *resp_p; */ /* snmp_dpi_trap_packet *trap_p; */ /* snmp_dpi_open_packet *open_p; */ /* snmp_dpi_close_packet *close_p; */ /* unsigned char *any_p; */ /* } data_u; */ /* }; */ /* typedef struct snmp_dpi_hdr snmp_dpi_hdr; */ /* #define snmp_dpi_hdr_NULL_p ((snmp_dpi_hdr *)0) */ /* */ /* Given the DPI parse tree we decide how to process the DPI packet. */ /* The following code sample demonstrates the high level process of */ /* a DPI subagent. */ /*********************************************************************/ main(int argc, char *argv[], char *envp[]) { unsigned char *packet_p; int i = 0; int rc = 0; #ifndef DPI_VERY_MINIMAL_SUBAGENT /* with VERY minimal agent */ int debug = 0; #endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */ unsigned long length; snmp_dpi_hdr *hdr_p; char *hostname_p = IP_LOOPBACK; char *community_p = SNMP_COMMUNITY; char *cmd_p = ""; if (argc >= 1) cmd_p = argv[0]; for (i=1; i < argc; i++) { if (strcmp(argv[i],"-h") == 0) { if (i+1 >= argc) { printf("Need hostname\n\n"); usage(cmd_p); } /* endif */ hostname_p = argv[++i]; #ifndef DPI_VERY_MINIMAL_SUBAGENT /* with VERY minimal agent */ } else if (strcmp(argv[i],"-c") == 0) { if (i+1 >= argc) { printf("Need community name\n\n"); usage(cmd_p); } /* endif */ community_p = argv[++i]; } else if (strcmp(argv[i],"-shm") == 0) { shared_mem = 1; } else if (strcmp(argv[i],"-d") == 0) { if (i+1 >= argc) { debug = 1; continue; } if ((strlen(argv[i+1]) == 1) && isdigit(*argv[i+1])) { i++; debug = atoi(argv[i]); } else { debug = 1; } /* endif */ #endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */ } else { usage(cmd_p); } /* endif */ } /* endfor */ #ifndef DPI_VERY_MINIMAL_SUBAGENT if (debug) { printf("\n%s - %s\n",__FILE__, VERSION); DPIdebug(debug); /* turn on DPI dubugging */ timeout += 6; /* longer timeout please */ } /* endif */ #endif /* ndef DPI_VERY_MINIMAL_SUBAGENT */ /* first init value2_p, our dpiSimpleString (DisplayString) */ /* since we treat it as display string keep terminating NULL */ value2_p = (char *) malloc(strlen("Initial String")+1); if (value2_p) { memcpy(value2_p,"Initial String",strlen("Initial String")+1); value2_len = strlen("Initial String")+1; } /* endif */ do_connect_and_open(hostname_p, community_p); /* connect and DPI-OPEN */ do_register(); /* register our subtree */ do_trap(); /* issue a trap as sample */ while (rc == 0) { /* do forever */ rc = DPIawait_packet_from_agent( /* wait for a DPI packet */ handle, /* on this connection */ -1, /* wait forever */ &packet_p, /* receives ptr to packet */ &length); /* receives packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p)/* If we fail to parse it */ exit(1); /* then exit */ switch(hdr_p->packet_type) { /* handle by DPI type */ case SNMP_DPI_GET: rc = do_get(hdr_p, hdr_p->data_u.get_p); break; case SNMP_DPI_GETNEXT: rc = do_next(hdr_p, hdr_p->data_u.next_p); break; case SNMP_DPI_SET: case SNMP_DPI_COMMIT: case SNMP_DPI_UNDO: rc = do_set(hdr_p, hdr_p->data_u.set_p); break; case SNMP_DPI_CLOSE: rc = do_close(hdr_p, hdr_p->data_u.close_p); break; case SNMP_DPI_UNREGISTER: rc = do_unreg(hdr_p, hdr_p->data_u.ureg_p); break; default: printf("Unexpected DPI packet type %d\n", hdr_p->packet_type); rc = -1; } /* endswitch */ if (rc) exit(1); } /* endwhile */ return(0); } /* end of main() */ /*********************************************************************/ /* Function to setup a connection and identify ourselves to agent */ /*********************************************************************/ /* Before a subagent can receive or send any DPI packets from/to the */ /* SNMP DPI capable agent, it must "connect" to the agent and then */ /* identify itself to the agent. */ /* */ /* Setting up the connection is one simple call to the the function */ /* DPIconnect_to_agent_TCP(). It expects two arguments: */ /* - a hostname (name or IpAdress in dot notation) that specifies */ /* where the agent is running. Often, the name "loopback" or */ /* "localhost" can be used if the subagent runs on the same */ /* system as the agent. */ /* - a community name which is used to obtain the dpi TCP port from */ /* the agent. Internally that is done by sending a regular SNMP */ /* GET request to the agent. In an open environment, we probably */ /* can use the well know community name "public". */ /* The function returns an negative error code if an error occurs. */ /* If the connection setup is successful, then it returns a handle */ /* which represents the connection, and which we must use on */ /* subsequent calls to send or await DPI packets. */ /* */ /* The second step is to identify the subagent to the agent. This is */ /* done by making a DPI-OPEN packet, sending it to the agent and */ /* then awaiting the response from the agent. The agent may accept */ /* or deny the OPEN request. */ /* Making a DPI-OPEN packet is done by calling mkDPIopen() which */ /* expects 6 arguments: */ /* - A unique subagent identification (an Object Identifier). */ /* - A description. Might be the NULL string ("") */ /* - Overall subagent timeout in seconds. The agent uses this value */ /* as a timeout value for a response when it sends a request to */ /* the subagent. Note that the agent may have a maximum value for */ /* this timeout that will be used if you exceed it. */ /* - The maximum number of varBinds per DPI packet that the */ /* subagent is willing (or capable) to handle. */ /* - The character set we want to use for string representation. */ /* Often it is easiest to choose the native character set. */ /* - Length of a password (zero means no password) */ /* - Ptr to the password (or NULL if no password) */ /* It depends on the agent if subagents must specify a password */ /* to open up a connection. */ /* The function returns a ptr to a static buffer holding the DPI */ /* packet if success. If it fails, it returns a NULL ptr. */ /* */ /* Once the DPI-OPEN packet has been created, you must send it to */ /* the agent. We can use the DPIsend_packet_to_agent() function */ /* which expects 3 arguments: */ /* - The handle of a connection (from DPIconnect_to_agent_TCP) */ /* - A ptr to the DPI packet (from mkDPIopen) */ /* - The length of the packet. */ /* The snmp_dpi.h include file provides a macro DPI_PACKET_LEN */ /* that calculates the packet length of a DPI packet. */ /* This function returs DPI_RC_OK (value zero) if success, otherwise */ /* an appropriate DPI_RC_xxx error code as defined in snmp_dpi.h. */ /* */ /* Now we must wait for a response to the DPI-OPEN. To await such */ /* a response, you call the DPIawait_packet_from_agent() function, */ /* which expects 4 arguments: */ /* - The handle of a connection (from DPIconnect_to_agent_TCP) */ /* - A timeout in seconds (max time to wait for response) */ /* - A ptr to a ptr, which will receive a ptr to a static buffer */ /* containing the awaited DPI packet. If the system fails to */ /* receive a packet, then a NULL ptr will be stored. */ /* - A ptr to a long integer (32-bit) which will receive the length */ /* of the awaited packet. On a failure it will be set to zero. */ /* This function returs DPI_RC_OK (value zero) if success, otherwise */ /* an appropriate DPI_RC_xxx error code as defined in snmp_dpi.h. */ /* */ /* The last step is to ensure that we indeed got a DPI-RESPONSE */ /* back from the agent, and if we did then we must ensure that the */ /* agent indeed accepted us as a valid subagent. This will be shown */ /* by the error_code field in the DPI response packet. */ /* */ /* The following code sample demonstrates how to setup a connection */ /* and then "open" it by identifying yourself to the agent. */ /*********************************************************************/ static void do_connect_and_open(char *hostname_p, char *community_p) { unsigned char *packet_p; int rc; unsigned long length; snmp_dpi_hdr *hdr_p; if (shared_mem) { /* if shared memory wanted */ handle = /* then (SHM) connect to */ DPIconnect_to_agent_SHM(1); /* always use 1 as queueID */ } else { handle = DPIconnect_to_agent_TCP( /* (TCP) connect to agent */ hostname_p, /* on this host */ community_p); /* snmp community name */ } /* endif */ if (handle < 0) exit(1); /* If it failed, exit */ packet_p = mkDPIopen( /* Make DPI-OPEN packet */ DPI_SIMPLE_SUBAGENT, /* Our identification */ "Simple DPI subAgent", /* description */ 10L, /* Our overall timeout */ 1L, /* max varBinds/packet */ DPI_NATIVE_CSET, /* native character set */ 0L, /* password length */ (unsigned char *)0); /* ptr to password */ if (!packet_p) exit(1); /* If it failed, exit */ rc = DPIsend_packet_to_agent( /* send OPEN packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ rc = DPIawait_packet_from_agent( /* wait for response */ handle, /* on this connection */ 10, /* timeout in seconds */ &packet_p, /* receives ptr to packet */ &length); /* receives packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p) /* If we fail to parse it */ exit(1); /* then exit */ if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1); rc = hdr_p->data_u.resp_p->error_code; if (rc != SNMP_ERROR_DPI_noError) exit(1); } /* end of do_connect_and_open() */ /*********************************************************************/ /* Register our sample dpiSimpleMIB subtree with the agent */ /*********************************************************************/ /* */ /* After we have setup a connection to the agent and after we have */ /* identified ourselves, we must register one or more MIB subtrees */ /* for which we want to be responsible (handle all SNMP requests). */ /* */ /* To do so, the subagent must create a DPI-REGISTER packet and send */ /* it to the agent. The agent will then send a response to indicate */ /* success or failure of the register request. */ /* */ /* To create a DPI-REGISTER packet, the subagent uses a call to the */ /* mkDPIregister() function, which expects these arguments: */ /* - A timeout value (in seconds) for this subtree. If you specify */ /* zero, then your overall timeout value (specified in DPI-OPEN) */ /* is used. Here you can specify a different value if you expect */ /* longer processing time for a specific subtree. */ /* - A requested priority. Multiple subagents may register the same */ /* subtree at different priorities (0 is better than 1 and so on).*/ /* The agent considers the subagent with the best priority to be */ /* the active subagent for the subtree. If you specify -1, then */ /* you ask for the best priority available. If you specify 0, */ /* then you ask for a better priority than any existing subagent */ /* may already have. */ /* - The MIB subtree which you want to control. You must specify thi*/ /* one with a trailing dot. */ /* - Our choice with rehard to GetBulk processing. We can ask the */ /* agent to pass it on or to map it into multiple GetNext packets.*/ /* The function returns a ptr to a static buffer holding the DPI */ /* packet if success. If it fails, it returns a NULL ptr. */ /* */ /* Now we must send this DPI-REGISTER packet to the agent. We use */ /* the DPIsend_packet_to_agent() function to do so, in a similar way */ /* as we did send the DPI_OPEN packet. Then we wait for a response */ /* from the agent. Again, we use the DPIawait_packet_from_agent() */ /* function in the same way as we awaited a response on the DPI-OPEN */ /* request. Once we have received the response, we must check the */ /* return code to ensure that registration was successful. */ /* */ /* The following code sample demonstrates how to register one MIB */ /* subtree with the agent. */ /*********************************************************************/ static void do_register(void) { unsigned char *packet_p; int rc; unsigned long length; snmp_dpi_hdr *hdr_p; packet_p = mkDPIregister( /* Make DPIregister packet */ timeout, /* timeout in seconds */ 0, /* requested priority */ DPI_SIMPLE_MIB, /* ptr to the subtree */ DPI_BULK_NO); /* Map GetBulk into GetNext*/ if (!packet_p) exit(1); /* If it failed, exit */ rc = DPIsend_packet_to_agent( /* send REGISTER packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ rc = DPIawait_packet_from_agent( /* wait for response */ handle, /* on this connection */ 10, /* timeout in seconds */ &packet_p, /* receives ptr to packet */ &length); /* receives packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p) /* If we fail to parse it */ exit(1); /* then exit */ if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1); rc = hdr_p->data_u.resp_p->error_code; if (rc != SNMP_ERROR_DPI_noError) exit(1); } /* end of do_register() */ /*********************************************************************/ /* Issuing a TRAP */ /*********************************************************************/ /* */ /* A trap can be issued at any time. To do so you must create a trap */ /* packet and send it to the agent. With the TRAP, you can pass all */ /* sorts of varBinds if you want. In this sample, we pass 2 varBinds */ /* one with integer data and one with an octet string. */ /* You can also pass an EnterpriseID, but with DPI 2.0, the agent */ /* will use your subagentID as the enterpriseID if you do not pass */ /* one with the trap. In most cases that will probably be fine. */ /* */ /* So we must first prepare a varBind list (chain) that contains */ /* the two variables that we want to pass along with the trap. */ /* To do so we must prepare a chanin of 2 snmp_dpi_set_packet */ /* structures, which looks like: */ /* */ /* struct dpi_set_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* unsigned char value_type; SNMP_TYPE_xxxx */ /* unsigned short value_len; value length */ /* char *value_p; ptr to value itself */ /* struct dpi_set_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_set_packet snmp_dpi_set_packet; */ /* #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) */ /* */ /* We can use the mkDPIset() function to prepare such a structure. */ /* This function expects the following arguments: */ /* - A ptr to an existing snmp_dpi_set_packet structure if the new */ /* varBind must be added to an existing chain of varBinds. */ /* If this is the first (or the only) varBind in the chain, then */ /* pass the snmp_dpi_set_packet_NULL_p ptr to indicate this. */ /* - a ptr to the subtree that we registered. */ /* - a ptr to the rest of the OID, in other words the piece that */ /* follows the subtree. */ /* - the value type of the value to be bound to the variable name. */ /* This is must be one of the SNMP_TYPE_xxx values as defined in */ /* the snmp_dpi.h include file. */ /* - the length of the value (for integer type values this must be */ /* a length of 4. So we always work with 32-bit signed or */ /* unsigned integers (except of course for the Counter64 type, */ /* for those we must point to a snmp_dpi_u64 structure and pass */ /* the length of that structure). */ /* - a ptr to the value. */ /* Memory for the varBind is dynamically allocated and the data */ /* itself is copied. So upon return we can dispose of our own ptrs */ /* and allocated memory as we please. If the call is successful, */ /* then a ptr is returned: */ /* - to a new snmp_dpi_set_packet if it is the first/only varBind */ /* - to the existing snmp_dpi_set_packet that we passed on the call.*/ /* In this case, the new packed has been chained to the end of */ /* the varBind list. */ /* If the mkDPIset() call fails, a NULL ptr is returned. */ /* */ /* Once we have prepared the SET-varBind data, we can create a DPI */ /* TRAP packet. To do so we can use the mkDPItrap() function, which */ /* expects these arguments: */ /* - the generic trap code, use 6 for enterpriseSpecific trap type. */ /* - the specific trap type. This is a type that is defined by */ /* the MIB which we are implementing. So in our sample we just */ /* use a 1. */ /* - a ptr to a chain of varBinds or the NULL ptr if no varBinds */ /* need to be passed with the trap */ /* - a ptr to the enterprise OID if we ewant to use a different */ /* enterpriseID than the OID we used to identify ourselves as */ /* a subagent at DPI-OPEN time. */ /* */ /* Here follows a code sample to create an enterpriseSepcific trap */ /* with specific type 1, and passing 2 varBinds, the first one with */ /* our object 1, instance 0, Integer32 value; the second one with */ /* our object 2, instance 0, Octet String. We pass no enterpriseID. */ /*********************************************************************/ static int do_trap(void) { unsigned char *packet_p; int rc; snmp_dpi_set_packet *varBind_p, *set_p; varBind_p = /* init the varBind chain */ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ DPI_SIMPLE_MIB, /* ptr to subtree */ DPI_SIMPLE_INTEGER, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ if (!varBind_p) return(-1); /* If it failed, return */ set_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ DPI_SIMPLE_MIB, /* ptr to subtree */ DPI_SIMPLE_STRING, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ value2_len, /* length of value */ value2_p); /* ptr to value */ if (!set_p) { /* if we failed... then */ fDPIset(varBind_p); /* free earlier varBinds */ return(-1); /* If it failed, return */ } set_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ DPI_SIMPLE_MIB, /* ptr to subtree */ DPI_SIMPLE_COUNTER32, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ if (!set_p) { /* if we failed... then */ fDPIset(varBind_p); /* free earlier varBinds */ return(-1); /* If it failed, return */ } #ifndef EXCLUDE_SNMP_V2_SUPPORT /* *Apr23*/ set_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ DPI_SIMPLE_MIB, /* ptr to subtree */ DPI_SIMPLE_COUNTER64, /* ptr to rest of OID */ SNMP_TYPE_Counter64, /* value type */ sizeof(value4), /* length of value */ &value4); /* ptr to value */ if (!set_p) { /* if we failed... then */ fDPIset(varBind_p); /* free earlier varBinds */ return(-1); /* If it failed, return */ } #endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */ packet_p = mkDPItrap( /* Make DPItrap packet */ 6, /* enterpriseSpecific */ 1, /* specific type = 1 */ varBind_p, /* varBind data, and use */ (char *)0); /* default enterpriseID */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send TRAP packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } /* end of do_trap() */ /*********************************************************************/ /* Processing a GET request */ /*********************************************************************/ /* */ /* A get request is pretty easy to process. When the DPI packet is */ /* parsed, the snmp_dpi_hdr structure will show in the packet_type */ /* that this is a SNMP_DPI_GET packet. In that case, the data_u field*/ /* contains a ptr to a GET-varBind, which is represented in an */ /* snmp_dpi_get_packet structure: */ /* */ /* struct dpi_get_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* struct dpi_get_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_get_packet snmp_dpi_get_packet; */ /* #define snmp_dpi_get_packet_NULL_p ((snmp_dpi_get_packet *)0) */ /* */ /* So, assuming we have registered example subtree dpiSimpleMIB */ /* and a GET request comes in for one variable dpiSimpleInteger.0 */ /* (so that is object 1 instance 0 in our subtree), then the fields */ /* in the snmp_dpi_get_packet would have ptrs that point to: */ /* */ /* object_p -> "1.3.6.1.4.1.2.2.1.5.1.0" */ /* group_p -> "1.3.6.1.4.1.2.2.1.5." */ /* instance_p -> "1.0" */ /* next_p -> snmp_dpi_get_packet_NULL_p */ /* */ /* If there are multiple varBinds in a GET request, then each one */ /* is represented in a snmp_dpi_get_packet structure and all the */ /* snmp_dpi_get_packet structures are chained via the next ptr. */ /* As long as the next ptr is not the snmp_dpi_get_packet_NULL_p */ /* pointer then there are more varBinds in the list. */ /* */ /* Now we can analyze the varBind stucture for whatever checking we */ /* want to do. Once we are ready to make a response that contains */ /* the value of the variable, then we first prepare a SET-varBind */ /* which is represented in an snmp_dpi_set_packet structure: */ /* */ /* struct dpi_set_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* unsigned char value_type; SNMP_TYPE_xxxx */ /* unsigned short value_len; value length */ /* char *value_p; ptr to value itself */ /* struct dpi_set_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_set_packet snmp_dpi_set_packet; */ /* #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) */ /* */ /* We can use the mkDPIset() function to prepare such a structure. */ /* This function expects the following arguments: */ /* - A ptr to an existing snmp_dpi_set_packet structure if the new */ /* varBind must be added to an existing chain of varBinds. */ /* If this is the first (or the only) varBind in the chain, then */ /* pass the snmp_dpi_set_packet_NULL_p ptr to indicate this. */ /* - a ptr to the subtree that we registered. */ /* - a ptr to the rest of the OID, in other words the piece that */ /* follows the subtree. */ /* - the value type of the value to be bound to the variable name. */ /* This is must be one of the SNMP_TYPE_xxx values as defined in */ /* the snmp_dpi.h include file. */ /* - the length of the value (for integer type values this must be */ /* a length of 4. So we always work with 32-bit signed or */ /* unsigned integers (except of course for the Counter64 type, */ /* for those we must point to a snmp_dpi_u64 structure and pass */ /* the length of that structure). */ /* - a ptr to the value. */ /* Memory for the varBind is dynamically allocated and the data */ /* itself is copied. So upon return we can dispose of our own ptrs */ /* and allocated memory as we please. If the call is successful, */ /* then a ptr is returned: */ /* - to a new snmp_dpi_set_packet if it is the first/only varBind */ /* - to the existing snmp_dpi_set_packet that we passed on the call.*/ /* In this case, the new packed has been chained to the end of */ /* the varBind list. */ /* If the mkDPIset() call fails, a NULL ptr is returned. */ /* */ /* Once we have prepared the SET-varBind data, we can create a DPI */ /* RESPONSE packet. To do so we can use the mkDPIresponse() function */ /* which expects these arguments: */ /* - a ptr to an snmp_dpi_hdr. We should use the hdr of the parsed */ /* incoming packet. It is used to copy the packet_id from the */ /* request into the response, such that the agent can correlate */ /* the response to a request. */ /* - a return code (snmp error code). If success, this should be */ /* SNMP_ERROR_noError (value zero). If failure, it must be one */ /* of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h */ /* include file. */ /* Note that a request for a non-existing object or instance is */ /* not considered an error. Instead, we must pass a value type */ /* of SNMP_TYPE_noSuchObject or SNMP_TYPE_noSuchInstance */ /* respectively. */ /* These 2 value types have an implicit value of NULL, so we can */ /* pass a zero length and a NULL ptr for the value in this case. */ /* - The index of the varBind in error (starts counting at 1). */ /* Pass zero if no error occured, else pass the proper index of */ /* the first varBind for which an error was detected. */ /* - a ptr to a (chain of) snmp_dpi_set_packets (varBinds) to be */ /* returned as response to the GET request. */ /* If an error was detected, then an snmp_dpi_set_packet_NULL_p */ /* ptr may be passed. */ /* */ /* Here follows a code sample to return a response. We assume that */ /* there are no errors in the request, but proper code should do the */ /* checking for that. For instance, we return a noSuchInstance if */ /* the instance is not exactly what we expect and a noSuchObject if */ /* the object instanceID is greater than 3 (like 4.0). But there */ /* might be no instanceID at all, and we should check for that too. */ /*********************************************************************/ static int do_get(snmp_dpi_hdr *hdr_p, snmp_dpi_get_packet *pack_p) { unsigned char *packet_p; int rc; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain */ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (pack_p->instance_p && (strcmp(pack_p->instance_p,"1.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"2.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ value2_len, /* length of value */ value2_p); /* ptr to value */ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"3.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ #ifndef EXCLUDE_SNMP_V2_SUPPORT } else if (pack_p->instance_p && /* *Apr23*/ (strcmp(pack_p->instance_p,"4.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_Counter64, /* value type */ sizeof(value4), /* length of value */ &value4); /* ptr to value *Apr23*/ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"4")>0)) { #else } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"3")>0)) { #endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */ varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_noSuchObject, /* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ } else { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_noSuchInstance,/* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ } /* endif */ if (!varBind_p) return(-1); /* If it failed, return */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ SNMP_ERROR_noError, /* all is OK, no error */ 0L, /* index is zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } /* end of do_get() */ /*********************************************************************/ /* Processing a GETNEXT request */ /*********************************************************************/ /* */ /* A getnext request is more difficult to process. When a DPI packet */ /* is parsed, the snmp_dpi_hdr structure shows in the packet_type */ /* that this is a SNMP_DPI_GETNEXT packet, and so the data_u field */ /* contains a ptr to a GETNEXT-varBind, which is represented in an */ /* snmp_dpi_next_packet structure: */ /* */ /* For simplicity and easier understanding we will discuss */ /* the GETNEXT for a scalar object, which only has one instance. */ /* For columnar objects, which may have multiple instances, */ /* the process is a bit more complicated. We assume that a DPI */ /* subagent programmer can handle that once he/she undestands */ /* the basics of GETNEXT processing in a DPI subagent. */ /* */ /* struct dpi_next_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* struct dpi_next_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_next_packet snmp_dpi_next_packet; */ /* #define snmp_dpi_next_packet_NULL_p ((snmp_dpi_next_packet *)0) */ /* */ /* So, assuming we have registered example subtree dpiSimpleMIB */ /* and a GETNEXT arrives for one variable dpiSimpleInteger.0 */ /* (so that is object 1 instance 0 in our subtree), then the fields */ /* in the snmp_dpi_get_packet structure would have ptrs pointing to: */ /* */ /* object_p -> "1.3.6.1.4.1.2.2.1.5.1.0" */ /* group_p -> "1.3.6.1.4.1.2.2.1.5." */ /* instance_p -> "1.0" */ /* next -> snmp_dpi_get_packet_NULL_p */ /* */ /* If there are multiple varBinds in a GETNEXT request, then each */ /* one is represented in a snmp_dpi_get_packet structure and all */ /* the snmp_dpi_get_packet structures are chained via the next ptr. */ /* As long as the next ptr is not the snmp_dpi_next_packet_NULL_p */ /* pointer then there are more varBinds in the list. */ /* */ /* Now we can analyze the varBind stucture for whatever checking we */ /* want to do. Then we must find out which OID is the one that */ /* lexicographically follows the one in the request. And it is that */ /* OID with its value that we must return as a response. So we must */ /* now also set the proper OID in the response. */ /* Once we are ready to make a response that contains the new OID */ /* and the value of that variable, then we must first prepare a */ /* SET-varBind which is represented in an snmp_dpi_set_packet: */ /* */ /* struct dpi_set_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* unsigned char value_type; SNMP_TYPE_xxxx */ /* unsigned short value_len; value length */ /* char *value_p; ptr to value itself */ /* struct dpi_set_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_set_packet snmp_dpi_set_packet; */ /* #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) */ /* */ /* We can use the mkDPIset() function to prepare such a structure. */ /* This function expects the following arguments: */ /* - A ptr to an existing snmp_dpi_set_packet structure if the new */ /* varBind must be added to an existing chain of varBinds. */ /* If this is the first (or the only) varBind in the chain, then */ /* pass the snmp_dpi_set_packet_NULL_p ptr to indicate this. */ /* - a ptr to the subtree that we registered. */ /* - a ptr to the rest of the OID, in other words the piece that */ /* follows the subtree. */ /* - the value type of the value to be bound to the variable name. */ /* This is must be one of the SNMP_TYPE_xxx values as defined in */ /* the snmp_dpi.h include file. */ /* - the length of the value (for integer type values this must be */ /* a length of 4. So we always work with 32-bit signed or */ /* unsigned integers (except of course for the Counter64 type, */ /* for those we must point to a snmp_dpi_u64 structure and pass */ /* the length of that structure). */ /* - a ptr to the value. */ /* Memory for the varBind is dynamically allocated and the data */ /* itself is copied. So upon return we can dispose of our own ptrs */ /* and allocated memory as we please. If the call is successful, */ /* then a ptr is returned: */ /* - to a new snmp_dpi_set_packet if it is the first/only varBind */ /* - to the existing snmp_dpi_set_packet that we passed on the call.*/ /* In this case, the new packed has been chained to the end of */ /* the varBind list. */ /* If the mkDPIset() call fails, a NULL ptr is returned. */ /* */ /* Once we have prepared the SET-varBind data, we can create a DPI */ /* RESPONSE packet. To do so we can use the mkDPIresponse() function,*/ /* which expects these arguments: */ /* - a ptr to an snmp_dpi_hdr. We should use the hdr of the parsed */ /* incoming packet. It is used to copy the packet_id from the */ /* request into the response, such that the agent can correlate */ /* the response to a request. */ /* - a return code (snmp error code). If success, this should be */ /* SNMP_ERROR_noError (value zero). If failure, it must be one */ /* of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h */ /* include file. */ /* Note that a request for a non-existing object or instance */ /* is not considered an error. Instead, we must pass the OID */ /* and value of the first OID that lexicographically follows */ /* the non-existing object and/or instance. */ /* Also, reaching the end of our subtree (there is no NEXT OID) */ /* is not considered an error. In this situation we must return */ /* the original OID (as received in request) and a value_type */ /* of SNMP_TYPE_endOfMibView. This value_type has an implicit */ /* value of NULL, so we can pass a zero length and a NULL ptr */ /* for the value. */ /* - The index of the first varBind in error (start counting at 1). */ /* Pass zero if no error occured, else pass the proper index of */ /* the first varBind for which an error was detected. */ /* - a ptr to a (chain of) snmp_dpi_set_packet(s) (varBinds) to */ /* be returned as response to the GETNEXT request. */ /* If an error was detected, then an snmp_dpi_set_packet_NULL_p */ /* ptr may be passed. */ /* */ /* Here follows a code sample to return a response. We assume that */ /* there are no errors in the request, but proper code should do */ /* the checking for that. We do proper checking for lexicographic */ /* next object, but we do no checking for ULONG_MAX, or making sure */ /* that the instance ID is indeed valid (digits and dots). */ /* If we get to the end of our dpiSimpleMIB then we must return an */ /* endOfMibView, as defined by the SNMPv2 rules. */ /*********************************************************************/ static int do_next(snmp_dpi_hdr *hdr_p, snmp_dpi_next_packet *pack_p) { unsigned char *packet_p; int rc; unsigned long subid; /* subid is unsigned */ unsigned long instance; /* same with instance */ char *cp; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain */ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (pack_p->instance_p) { /* we have an instance ID */ cp = pack_p->instance_p; /* pick up ptr */ subid = strtoul(cp, &cp, 10); /* convert subid (object) */ if (*cp == '.') { /* followed by a dot ? */ cp++; /* point after it if yes */ instance=strtoul(cp,&cp,10); /* convert real instance */ /* not that we need it, we */ subid++; /* only have instance 0, */ /* so NEXT is next object */ instance = 0; /* and always instance 0 */ } else { /* no real instance passed */ instance = 0; /* so we can use 0 */ if (subid == 0) subid++; /* if object 0, start at 1 */ } /* endif */ } else { /* no instance ID passed */ subid = 1; /* so do first object */ instance = 0; /* instance 0 (all we have)*/ } /* endif */ /* we have set subid and instance such that we can basically */ /* process the request as a GET now. Actually, we don't even */ /* need instance, because all out object instances are zero. */ if (instance != 0) printf("Strange instance: %lu\n",instance); switch (subid) { case 1: varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ DPI_SIMPLE_INTEGER, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ break; case 2: varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ DPI_SIMPLE_STRING, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ value2_len, /* length of value */ value2_p); /* ptr to value */ break; case 3: varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ DPI_SIMPLE_COUNTER32, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ break; #ifndef EXCLUDE_SNMP_V2_SUPPORT case 4: /* *Apr23*/ varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ DPI_SIMPLE_COUNTER64, /* ptr to rest of OID */ SNMP_TYPE_Counter64, /* value type */ sizeof(value4), /* length of value */ &value4); /* ptr to value */ break; /* *Apr23*/ #endif /* ndef EXCLUDE_SNMP_V2_SUPPORT */ default: varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to subtree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_endOfMibView, /* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ break; } /* endswitch */ if (!varBind_p) return(-1); /* If it failed, return */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ SNMP_ERROR_noError, /* all is OK, no error */ 0L, /* index is zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } /* end of do_next() */ /*********************************************************************/ /* Processing a SET/COMMIT/UNDO request */ /*********************************************************************/ /* */ /* These 3 requests can come in one of these sequences: */ /* - SET, COMMIT */ /* - SET, UNDO */ /* - SET, COMMIT, UNDO */ /* Normal sequence is SET and then COMMIT. When we receive a SET */ /* request, we must make preparations to accept the new value */ /* like check that it is for an existing object and instance, check */ /* the value type and contents to be valid, allocate memory etc), */ /* but we must not yet effectuate the change. */ /* If all goes well, the next request we receive will be a COMMIT */ /* request. It is then that we must effectuate the change, but we */ /* must then also keep enough information such that we can UNDO */ /* the change later if we get a subsequent UNDO request. The latter */ /* may happen if the agent discovers any errors with other */ /* subagents while processing requests that belong to the same */ /* original SNMP SET packet (all the varBinds in the same SNMP */ /* request PDU must be processed "as if atomic"). */ /* */ /* When the DPI packet is parsed, the snmp_dpi_hdr structure shows */ /* in the packet_type that this is an SNMP_DPI_SET, SNMP_DPI_COMMIT */ /* SNMP_DPI_UNDO packet. In that case, the data_u field contains a */ /* ptr to a SET-varBind, represented in an snmp_dpi_get_packet */ /* structure (COMMIT and UNDO have same varBind data as SET upon */ /* which they follow): */ /* */ /* struct dpi_set_packet { */ /* char *object_p; ptr to OIDstring */ /* char *group_p; ptr to subtree */ /* char *instance_p; ptr to rest of OID */ /* unsigned char value_type; SNMP_TYPE_xxxx */ /* unsigned short value_len; value length */ /* char *value_p; ptr to value itself */ /* struct dpi_set_packet *next_p; ptr to next in chain */ /* }; */ /* typedef struct dpi_set_packet snmp_dpi_set_packet; */ /* #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) */ /* */ /* So, assuming we have registered example subtree dpiSimpleMIB */ /* and a GET request comes in for one variable dpiSimpleString.0 */ /* (so that is object 1 instance 0 in our subtree), and also */ /* assuming that the agent knows about our compiled dpiSimpleMIB */ /* so that it knows this is a DisplayString as opposed to just an */ /* arbitrary OCTET_STRING, then the ptrs in the snmp_dpi_set_packet */ /* structure would have ptrs and values like: */ /* */ /* object_p -> "1.3.6.1.4.1.2.2.1.5.2.0" */ /* group_p -> "1.3.6.1.4.1.2.2.1.5." */ /* instance_p -> "2.0" */ /* value_type -> SNMP_TYPE_DisplayString */ /* value_len -> 8 */ /* value_p -> ptr to the value to be set. */ /* next_p -> snmp_dpi_get_packet_NULL_p */ /* */ /* If there are multiple varBinds in a SET request, then each one */ /* is represented in a snmp_dpi_set_packet structure and all the */ /* snmp_dpi_set_packet structures are chained via the next ptr. */ /* As long as the next ptr is not the snmp_dpi_set_packet_NULL_p */ /* pointer then there are more varBinds in the list. */ /* */ /* Now we can analyze the varBind stucture for whatever checking we */ /* want to do. Once we are ready to make a response that contains */ /* the value of the variable, we may prepare a new SET-varBind. */ /* However, by definition, the response to a successful SET is */ /* exactly the same as the SET request. So there is no need to */ /* return any varBinds. A simple response with SNMP_ERROR_noError */ /* and an index of zero will do. In case that there is an error, */ /* then a simple response with the SNMP_ERROR_xxxx error code and */ /* an index pointing to the varBind in error (counting starts at 1) */ /* will do. */ /* */ /* Here follows a code sample to return a response. We assume that */ /* there are no errors in the request, but proper code should do */ /* the checking for that. We also do not check if the varBind in */ /* the COMMIT and/or UNDO is the same as that in the SET request. */ /* A proper agent would make sure that that is the case, but a */ /* proper subagent may want to verify that for itself. */ /* We only do one simple check that this is dpiSimpleString.0 and if */ /* it is not we return a noCreation, which may not be correct. */ /* The mainline does not even return a response. */ /*********************************************************************/ static int do_set(snmp_dpi_hdr *hdr_p, snmp_dpi_set_packet *pack_p) { unsigned char *packet_p; int rc; int index = 0; int error = SNMP_ERROR_noError; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain */ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (!pack_p->instance_p || (strcmp(pack_p->instance_p,"2.0") != 0)) { if (pack_p->instance_p && (strncmp(pack_p->instance_p,"1.",2) == 0)) { error = SNMP_ERROR_notWritable; } else if (pack_p->instance_p && (strncmp(pack_p->instance_p,"2.",2) == 0)) { error = SNMP_ERROR_noCreation; } else if (pack_p->instance_p && (strncmp(pack_p->instance_p,"3.",2) == 0)) { error = SNMP_ERROR_notWritable; } else { error = SNMP_ERROR_noCreation; } /* endif */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ error, /* all is OK, no error */ 1, /* index is 1, 1st varBind */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } switch (hdr_p->packet_type) { case SNMP_DPI_SET: if ((pack_p->value_type != SNMP_TYPE_DisplayString) && (pack_p->value_type != SNMP_TYPE_OCTET_STRING)) { /* check octet string in case agent has no compiled MIB */ error = SNMP_ERROR_wrongType; break; /* from switch */ } /* endif */ if (new_val_p) free(new_val_p); /* free these memory areas */ if (old_val_p) free(old_val_p); /* if we allocated any */ new_val_p = (char *)0; old_val_p = (char *)0; new_val_len = 0; old_val_len = 0; new_val_p = /* allocate memory for */ malloc(pack_p->value_len); /* new value to set */ if (new_val_p) { /* If success, then also */ memcpy(new_val_p, /* copy new value to our */ pack_p->value_p, /* own and newly allocated */ pack_p->value_len); /* memory area. */ new_val_len = pack_p->value_len; } else { /* Else failed to malloc, */ error = SNMP_ERROR_genErr; /* so that is a genErr */ index = 1; /* at first varBind */ } /* endif */ break; case SNMP_DPI_COMMIT: old_val_p = cur_val_p; /* save old value for undo */ cur_val_p = new_val_p; /* make new value current */ new_val_p = (char *)0; /* keep only 1 ptr around */ old_val_len = cur_val_len; /* and keep lengths correct*/ cur_val_len = new_val_len; new_val_len = 0; /* may need to convert from ASCII to native if OCTET_STRING */ break; case SNMP_DPI_UNDO: if (new_val_p) { /* free allocated memory */ free(new_val_p); new_val_p = (char *)0; new_val_len = 0; } /* endif */ if (old_val_p) { if (cur_val_p) free(cur_val_p); cur_val_p = old_val_p; /* reset to old value */ cur_val_len = old_val_len; old_val_p = (char *)0; old_val_len = 0; } /* endif */ break; } /* endswitch */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ error, /* all is OK, no error */ index, /* index is zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } /* end of do_set() */ /*********************************************************************/ /* Function to handle a DPI UNREGISTER request */ /*********************************************************************/ /* An agent can send an UNREGISTER packet if some other subagent does*/ /* a register for the same subtree at a higher priority. In this case*/ /* we can decide to keep the connection opn, we may regain control */ /* over the subtree if that higher priority registration goes away. */ /* An agent can also send an UNREGISTER if for instance an SNMP */ /* manager tells it to "invalidate" the subagent connection or the */ /* registered subtree. In this case we decide to give up. */ /* */ /* Here is a very simple sample piece of code to handle such a packet*/ /*********************************************************************/ static int do_unreg(snmp_dpi_hdr *hdr_p, snmp_dpi_ureg_packet *pack_p) { printf("DPI UNREGISTER received from agent, reason=%d\n", pack_p->reason_code); printf(" subtree=%s\n",pack_p->group_p); if (pack_p->reason_code == SNMP_UNREGISTER_higherPriorityRegistered) { return(0); /* keep waiting, we may regain subtree later */ } /* endif */ DPIdisconnect_from_agent(handle); return(-1); /* causes exit in main loop */ } /* end of do_unreg() */ /*********************************************************************/ /* Function to handle a DPI CLOSE request */ /*********************************************************************/ /* An agent can send a CLOSE packet if it encounters an error or for */ /* some other reason. It can also do so if an SNMP MANAGER tells it */ /* to "invalidate" the subagent connection. */ /* */ /* Here is a very simple sample piece of code to handle such a packet*/ /*********************************************************************/ static int do_close(snmp_dpi_hdr *hdr_p, snmp_dpi_close_packet *pack_p) { printf("DPI CLOSE received from agent, reason=%d\n", pack_p->reason_code); DPIdisconnect_from_agent(handle); return(-1); /* causes exit in main loop */ } /* end of do_close() */ /* \end{verbatim} */