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 packet_body contains a pointer to a GETNEXT-varBind, which is represented in an snmp_dpi_next_packet structure:
struct dpi_next_packet {
char *object_p; /* ptr to OIDstring */
char *group_p; /* ptr to sub-tree */
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)
In the interest of 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 more complex. However, the DPI subagent programmer should be able to handle that once the basics of GETNEXT processing in a DPI subagent is understood.
Assuming we have registered example sub-tree dpiSimpleMIB and a GETNEXT arrives for one variable, dpiSimpleInteger.0, so that is object 1 instance 0 in our sub-tree, the fields in the snmp_dpi_get_packet structure would have pointers 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_next_packet_NULL_p
If there are multiple varBinds in a GETNEXT request, each one is represented in a snmp_dpi_get_packet structure and all the snmp_dpi_get_packet structures are chained via the next pointer. As long as the next pointer is not the snmp_dpi_next_packet_NULL_p pointer, there are more varBinds in the list.
Now we can analyze the varBind structure for whatever checking we want to do. We must find out which OID is the one that lexicographically follows the one in the request. It is that OID with its value that we must return as a response. Therefore, 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, we must 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 sub-tree */
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 parameters:
Memory for the varBind is dynamically allocated and the data itself is copied. Upon return, we can dispose of our own pointers and allocated memory as we please. If the call is successful, a pointer is returned as follows:
If the mkDPIset() call fails, a NULL pointer is returned.
Once we have prepared the SET-varBind data, we can create a DPI RESPONSE packet using the mkDPIresponse() function, which expects these parameters:
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.
Reaching the end of our sub-tree is not considered an error. For example, if there is no NEXT OID, this is not an error. In this situation we must return the original OID as received in the 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 pointer for the value.
The following code example returns 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, we must return an endOfMibView as defined by the SNMP Version 2 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 */
instance = 0; /* passed, so we use 0 */
if (subid == 0) subid++; /* if object 0, subid 1 */
} /* endif */
} else { /* no instance ID passed */
subid = 1; /* so do first object */
instance = 0; /* 0 is 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 sub-tree */
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 varBindchain*/
pack_p->group_p, /* ptr to sub-tree */
DPI_SIMPLE_STRING, /* ptr to rest of OID */
SNMP_TYPE_DisplayString,/* value type */
strlen(value2_p), /* length of value */
value2_p); /* ptr to value */
break;
case 3:
varBind_p = mkDPIset( /* Make DPI set packet*/
varBind_p, /* ptr to varBindchain*/
pack_p->group_p, /* ptr to sub-tree */
DPI_SIMPLE_COUNTER32, /* ptr to rest of OID */
SNMP_TYPE_Counter32, /* value type */
sizeof(value3), /* length of value */
&value3); /* ptr to value */
break;
case 4: /* *Apr23*/
varBind_p = mkDPIset( /* Make DPI set packet */
varBind_p, /* ptr to varBind chain */
pack_p->group_p, /* ptr to sub-tree */
DPI_SIMPLE_COUNTER64, /* ptr to rest of OID */
SNMP_TYPE_Counter64, /* value type */
sizeof(value4), /* length of value */
&value4); /* ptr to value */
break; /* *Apr23*/
default:
varBind_p = mkDPIset( /* Make DPI set packet*/
varBind_p, /* ptr to varBindchain*/
pack_p->group_p, /* ptr to sub-tree */
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 pack */
hdr_p, /* ptr parsed request */
SNMP_ERROR_noError, /* all is OK, no error */
0L, /* index 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() */