scotty agent: processing getbulk PDUs

Robert Premuz (rpremuz@srce.hr)
Fri, 9 Feb 1996 13:05:45 +0100 (MET)

Hello,

This is a request for scotty SNMP agent enhancements in processing of
incomming getbulk PDUs (I use scotty v. 2.0.2).

After I played a bit with scotty agent in SNMPv2 mode, which should
support getbulk requests, I noticed that the getbulk requests are not
answered properly by the agent (you can try to run the example agent
and manager scripts at the end of the message).

My understanding of the C code in the snmp/snmpAgent.c file in the
distribution, and the GetRequest function especially, tells me that
the getbulk PDUs are processed in the same way as the getnext PDUs.
It means that the agent does not take into account the number of
non-repeater variables and the max. number of repetitions for
repeating variables.

Although the scotty(1) man page does not explicitly say how getbulk
requests are processed by scotty agent (as it does not say that about
any other kind of requests), I expected that the non-repeaters and
max-repetitions parameters were supported as scotty in manager mode
was able to send getbulk requests by

snmp# getbulk nr mr varlist [callback]

The current processing of getbulk PDUs in the same way as getnext
PDUs are processed does not achieve what was intended when the
getbulk PDU was introduced in SNMP, i.e. to reduce the number of UDP
packets exchanged between an SNMP agent and an SNMP manager when the
manager is traversing a MIB subtree implemented in the agent.

To remind you how get-bulk PDUs should be processed by an SNMP agent,
this is what Marshall T. Rose says on the page 259 in the 2nd edition
of 'The Simple Book' (Prentice-Hall, 1994):

<start of citation> --------------------------------------------------

When an agent receives a get-bulk, it calculates the minimum of:

* the sender's maximum message size (partyMaxMessageSize); and,

* the agent's own maximum message generation size.

>From this it subtracts the sum of two quantities:

* the size of the privacy/authentication wrappers used when
generating a reply; and,

* the size of a response with no variable bindings.

The difference indicates the maximum amount of space available for
variable bindings in the response. If this difference is less than
zero, the request is silently dropped (after all, not even an empty
response could be sent back). Otherwise, a response is generated,
which will have zero or more variable bindings.

The agent then cycles through the first non-repeaters variables in
the request, using the get-next operator on each, appending the new
instance and value to the response, and decreasing the amount of free
space accordingly. If there isn't enough room, the response is sent
before it would overflow.

Then, for up to max-repetition[s] times, the agent cycles through any
repeating variables in the request (all the variables after the first
non-repeater variable). For each repetition, the get-next operator is
used on the results of the previous repetition: the new instance and
value are appended to the response, and the amount of free space is
decreased accordingly.

Ultimately, either the free space is exhausted or the maximum number
of repetitions is performed. It is important to appreciate that the
agent may terminate a repetition at any time - before the first
variable, after the last variable, or anywhere in between.

<end of citation> ----------------------------------------------------

I have one more wish regarding this issue. It's about how the
snmp# walk command is implemented.

The command repeats sending getbulk requests as explained in the
scotty(1) man page. But the manual does not say what are the values
of non-repeaters and max-repetitions parameters in these requests.

As I can see in the snmp/snmpTcl.c file in the distribution, the
SNMPWalk function implements the command and the following piece of
code explains the secret:

pdu->type = SNMPv2_GETBULK;
pdu->request_id = ++session->reqid;

/*
* Set the non-repeaters and the max-repetitions for the getbulk
* operation. I do not know if 16 is a good constant. Perhaps we
* should start with a small value and increase in every loop?
*/

pdu->error_status = 0;
pdu->error_index = (16 / oidc > 0) ? 16 / oidc : 1;

So, if my understanding is right, non-repeaters is set to 0, while
max-repetitions is set to (16 / oidc > 0) ? 16 / oidc : 1 , where
oidc is the number of OIDs in varbind list given to the command to
walk through. In that way, the max. number of varbinds returned by
the agent in its response to the getnext request is limited to 16.

Now, I suggest some changes in the function:

1) Define the magic constant 16 by a #define statement in some .h
file. I wonder why such a small value is used. Was the intention to
reduce the size of the returned packet?

2) It would be nice if this magic constant could be set to some other
value by an option, e.g. -maxoids n, when invoking the command. If
the option is not used, then the default value of 16 can be used.

3) The above comment says: "Perhaps we should start with a small
value and increase in every loop?" This would be really great
although it needs more programmer's time for implementation.

That's all for now. I hope my thoughts said in all those English
words were understandable to you.

Looking forward to your replys.

v
-- rpr. : Robert Premuz
Internet: rpremuz@malik.srce.hr * Voice at home: +385 (0)1 687564

----------------------------------------------------------------------
#! /usr/local/bin/scotty -f
#
# example_agent.tcl
#
# example SNMP agent based on scotty 2.0.2

mib load unix.mib

set agent [snmp session -version SNMPv2C -port 12345 -agent ""]

$agent bind "" begin {puts "$argv0 received an %T request.\nVarbinds: %V\n"}

$agent instance fsIdentifier.0 _fsTable(fsIdentifier.0) 0
$agent instance fsIdentifier.1 _fsTable(fsIdentifier.1) 1
$agent instance fsIdentifier.2 _fsTable(fsIdentifier.2) 2
$agent instance fsIdentifier.3 _fsTable(fsIdentifier.3) 3

$agent instance fsBlockSize.0 _fsTable(fsBlockSize.0) 512
$agent instance fsBlockSize.1 _fsTable(fsBlockSize.1) 1024
$agent instance fsBlockSize.2 _fsTable(fsBlockSize.2) 2048
$agent instance fsBlockSize.3 _fsTable(fsBlockSize.3) 4096

puts "Waiting for SNMP requests\n"

----------------------------------------------------------------------

----------------------------------------------------------------------
#! /usr/local/bin/scotty -f
#
# example_manager.tcl
#
# example SNMP manager based on scotty 2.0.2

# Report background errors with complete backtrace to standard error.
#
proc tkerror { msg } {
global errorInfo
puts stderr "Background error:\n$errorInfo"
}

# Show SNMP response returned from the agent.
#
proc show_response {varbinds requestID errorStatus errorIndex packetType} {
puts "Packet type = $packetType ; Request ID = $requestID"
puts "Error status = $errorStatus ; Error index = $errorIndex"

foreach varbind $varbinds {
set variable_name [ mib name [ lindex $varbind 0 ] ]
set variable_value [ lindex $varbind 2 ]
puts "Variable $variable_name = $variable_value"
}
puts ""
}

# Show walk results.
#
proc show_walk {varbinds} {
puts "Walk result:"
foreach varbind $varbinds {
set var_name [ mib name [ lindex $varbind 0 ] ]
set var_value [ lindex $varbind 2 ]
puts "Variable $var_name = $var_value"
}
puts ""
}

mib load rfc1213.mib ;# sysUpTime
mib load unix.mib ;# fsTable

set sess [snmp session -version SNMPv2C -port 12345 -retries 5 -timeout 10]

set varlist [list sysUpTime fsIdentifier fsBlockSize]

# ask the agent for max. 5 instances of fsIdentifier and fsBlockSize variables
#
$sess getbulk 1 5 $varlist {show_response {%V} {%R} {%E} {%I} {%T}}

set varlist fsTable
if [catch {$sess walk varbinds $varlist {show_walk $varbinds}} err_msg] {
puts stderr "Error in $argv0:\n$err_msg"
}

snmp wait
exit
----------------------------------------------------------------------