Network Management Week 4

Fall 2009; LT-412, Wed 4:15-6:45 pm
 
Network Planning
SNMP packet format
    error codes
    trap codes
net-snmp agent configuration
BER
    OID encoding
    ireasoning walk
    bulkget
    Additional ASN.1 features
mib-2 groups
    address translation (at)
    IP
    TCP
    UDP
importing MIB files into iReasoning




Network planning checklists, Burke, 2004
 


 

 
SNMP packet formats:

    GET-request
    GET-NEXT-request
    SET-request
    GET-response, 
    TRAP
     
    format of one of first four:
     
    ReqID  ErrStatus  ErrIndex  VarBindList
     
    ErrorStatus: 0 in request, 0 in response if no error. Other error codes:
        1: PDU has too many bytes
        2. no object with given OID
        3. PDU type field is bad
        4. readonly
        5. other errors

Demo with snmpget; note the need for the -Cf option because otherwise snmpget will attempt to recover from the error by retransmitting the request with the bad entry deleted:

    snmpget -Cf -v 1 -c public localhost system.3.0 system.4.1

(called badoid in my file)
     
    VarBindList: list of (OID,Value) pairs (values are Null in requests)
     
   
    GET-req: send out VarBindList with OIDs, null values. Multiple values are requested with a VarBindList of length > 1; some common tools don't do that however.
    GET-resp: return same OIDs, with values filled in
 
SNMPv1 semantics: if there is an error in a single request entry, the response is simply "error"; there are no partial answers. There is a table of all six error codes on page 62 of Mauro & Schmidt:
This list is pretty spare.

SNMPv2c introduced partial answers, and the concept of ErrorIndex (1st entry in the VarBindList that caused an error). Note that there are still no partial SET actions; those remain all-or-nothing.

SNMPv2 added some more error types, extending the list above:

This might be a good point to enumerate the SNMP traps, on p 64 of K&S:
The linkUp/linkDown and enterpriseSpecific ones are the most useful. K&S give an example of an enterpriseSpecific trap on p 65: rdbmsOutOfSpace, part of RFC 1697. Here is the trap definition:
 
rdbmsOutOfSpace NOTIFICATION-TYPE
    OBJECTS { rdbmsSrvInfoDiskOutOfSpaces }
    STATUS current
    DESCRIPTION
        "An rdbmsOutOfSpace trap signifies that one of the database
        servers managed by this agent has been unable to allocate
        space for one of the databases managed by this agent. Care
        should be taken to avoid flooding the network with these traps."
     ::= { rdbmsTraps 2 }

Earlier, there is a definition of rdbmsTraps ::= { rdbmsMIB 2 }. Note that this is slightly different from K&S.
And here is the mib definition of rdbmsSrvInfoDiskOutOfSpaces (RFC 1697, p 22)
  rdbmsSrvInfoDiskOutOfSpaces OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The total number of times the server has been unable to
obtain disk space that it wanted, since server startup. This
would be inspected by an agent on receipt of an
rdbmsOutOfSpace trap."
::= { rdbmsSrvInfoEntry 9 }

Note that trap flooding would be a serious concern here.


 

net-snmp configuration (snmpd)

Here is an example of adding a new community, foo, to the snmpd.conf file. I did this on my laptop; that is, localhost. I add


# First, map the community name (COMMUNITY) into a security name
# (local and mynetwork, depending on where the request is coming
# from):

#       sec.name  source          community
# com2sec paranoid  default         public
# pld
com2sec readonly  default          public
# pld demo community "foo"
com2sec foocom     default         foo
#com2sec readwrite default         private

####
# Second, map the security names into group names:

#                 sec.model  sec.name
group MyROSystem v1        paranoid
group MyROSystem v2c       paranoid
group MyROSystem usm       paranoid
group MyROGroup v1         readonly
group MyROGroup v2c        readonly
group MyROGroup usm        readonly
group MyRWGroup v1         readwrite
group MyRWGroup v2c        readwrite
group MyRWGroup usm        readwrite

group MyFOOgroup v1        foocom

####
# Third, create a view for us to let the groups have rights to:

#           incl/excl subtree                          mask
view all    included  .1                               80
view system included  .iso.org.dod.internet.mgmt.mib-2.system

view fooview included  .1.3.6.1.2.1.1
view fooview included  .1.3.6.1.2.1.2

####
# Finally, grant the 2 groups access to the 1 view with different
# write permissions:

#                context sec.model sec.level match  read   write  notif
access MyROSystem ""     any       noauth    exact  system none   none
access MyROGroup ""      any       noauth    exact  all    none   none
access MyRWGroup ""      any       noauth    exact  all    all    none

access MyFOOgroup ""     any       noauth    exact  fooview none   none

Note that we have two view statements, adding two specific subtrees to fooview. If we then do an snmpwalk to retrieve everything, using community "foo", we get these two subtrees.
    snmpwalk -v 1 -c foo localhost .1.3.6.1.2.1

Side note: explain the mask option in the view statement. An OID has multiple levels. We must match all the levels specified, unless some of the levels are "unselected" via a mask. A mask is a bitstring of the same length as the OID; multiple bytes are separated by ":". OID levels corresponding to a mask bit of 1 must match; however, OID levels corresponding to a mask bit of 0 need not match (that is, 0 entries in the mask mean that the corresponding OID entry is treated as a "wildcard"). Thus
    view VNAME  included .1.3.6.1.2.1   F4
would match 1.3.6.1.2.1 and also 1.3.6.1.4.1, 1.3.6.1.6.1, etc. Note that the F4 bits are 1.1.1.1.0.1.0.0, so level 5 (the first 0 of the mask) need not match in the OID.

This is more likely to be useful in masking to allow access to a specific row of a table, while allowing access to all columns of that row. Recall that the OID of a table entry is Entry.column.row.


ASN.1/BER syntax


Basic issue: we need a machine-independent way of describing complex datatypes. Host byte order cannot matter!

(Actually, SNMP only uses atomic data types as OID values; records and arrays of records (ie tables) are implemented as OID tree structures.)
 
Data described with ASN.1, used to define languages (in effect)
 
ASN.1 basic data item encoding:
 
TAG
LEN
VALUE
 
This is applied recursively, as necessary.

The TAG identifies Type: basic ASN.1 type, class, etc
    Bits:   
7 6 5
4 3 2 1 0

    1st two bits (7 & 6): the "class":  
class:

universal
00
application
01
context-specific
10
private
11
    
Bit 5 indicates whether the type is "simple" or is a constructed type:
    0: simple
    1: constructed

The remaining bits indicate the actual type.
Universal:
    00001:    boolean (simple)
    00010:   integer (simple)
    00011:   bit string (simple)
    00100:   octet string (simple)
    00101:   Null (simple)
    00110:   OID (simple)
    01001:   floating point
    10000:    Sequence / Sequence Of (constructed)
    10110:    IA5string (aka ASCII)

Here are a few context-specific type tags, all constructed types:
    00000   Get-Request PDU
    00001   Get-Next-Request PDU
    00010   Get-Response PDU
    00011   Set-Request PDU
    00100   Trap PDU

Here are some application types, specific to SNMP
    00000   IpAddress
    00001   Counter/Counter32
    00010   Gauge/Gauge32
    00011   TimeTicks
    00100   Opaque
    00101   NsapAddress  
    00110   Counter64 (SNMPv2 only)
   
 
There are some examples in Burke, Appendix D

SNMP packet:
    SNMP message tag      |  SNMP msg LENGHT  |  SNMP message VALUE
          Value:
                version, community name, PDU
                   version: integer
                   community: octet_string
                   PDU: varbindlist: has TAG, LEN, and then a whole series of TLVs for ReqID, ErrorStatus, ErrorIndex, VarBindList

                         VarBindList: has a TLV for the whole list, and then individual TLVs for each item in the list.

Here's a picture from http://www.rane.com/n161fig5.gif:

hierarchical SNMP encoding
 
Note that there are 46 bytes in all, or 44 bytes in the top-level message. 44 = 0x2C.

LEN field:
    1-byte length: 1st bit is 0, remaining bits indicate len < 128 bytes
    multi-byte len: 1st bit 1, remaining seven bits indicate # of
    following octets are length.
    Example: a data length of 128 would be specified with two LEN bytes:     1000 0001  1000 0000
     
Encoding of 17, 132, and 65540
17:    2, 1, 17             
260:    2, 2, 1, 4              260 = 1*256 + 4s
65540:    2, 3, 1, 0, 4        65540 = 1*2562 + 0*256 + 4
 
Note this is intrinsically variable-length. The len field is used even for integers; may hold 1 (byte) or 4 (bytes) or more. Data alignment is nonexistent.

OID encoding

The tag is 00-0-00110, or 0x06, followed by a length byte. The tag for a SEQUENCE is 00-1-10000, or 0x30.
The first two levels x.y of an OID are encoded as a single byte: 40*x+y. For "1.3" this is 43, or 0x2B.
Subsequent levels are encoded as one-byte numbers, if less than 128. For numbers greater than or equal to 128, the the 8th bit is used as a "more" flag. (1=more, 0=end), so 2680 = 20*128 + 120 would encode as the two 7-bit pieces <20,120>, or <0x14, 0x78>, but the high bit would be set on the 0x14 byte making it 0x94. The final encoding would be <0x94,0x78>. Similarly, to encode 2021 (the private number for ucd-snmp), we would split it into 7-bit pieces <0x0f,0x65> and set the high bit on the first piece to get <0x8f,0x65>. Or, for 8072 (the private number for net-snmp), the 7-bit pieces are 0x1f and 0x88; setting the first byte's high bit leaves us <0x9f,0x88>.

Thus, to encode 1.3.6.1.2.1.1.1.0, use (length byte is in bold)
    0x06  0x08  0x2b  0x06 0x01 0x02 0x01 0x01 0x01 0x00
 
Decoding of my big-request demo packet:

loopback.wireshark: send request with 128 entries in it; get back huge response packet of size 11085. This would be fragmented if sent on a regular ethernet.

Actually, on a regular ethernet (see big128ulam3.wireshark) it is very fragmented. The request is 1835 bytes, and the response is10667 bytes.

ireasoning walk

If we enable wireshark and do a walk on the iftable, we see lots of packets. There are 68 get-next-requests and 68 get-responses. Note in particular the final get-response. There are 66 data items in the table; we do a get-next on each of them (remember that the following OID is actually retrieved), plus one on the interfaces group mib-2.2 and one on ifNumber.

The wireshark file is iftable.ireasoning.wireshark.

bulkget

Next we try SNMPv2c's bulkget operation. See bulkget.wireshark. Note that there is a single request and a single response; the response is a single 497-byte packet.

The bulkget request is generated with:
    snmpbulkget -v2c -Cn1 -Cr18 -c public localhost system ifTable
the -Cn1 means that there is 1 nonrepeater; in this case the system OID. The remaining OIDs on the line are the repeaters; there is only the one here, ifTable. The -Cr18 means that we are to request it (with get-next semantics) 18 times, with all the repetition done on the agent side.



 
 Why do all this encoding? So any data can be received and decoded without knowledge of semantics. 
 
Why do that? Look at the ireasoning mib browser! If we didn't have embedded types, most of the data would print in binary format. At least this way, we can try to figure out what some of the data values mean.

Actually, compatibility between big-endian and wrong-endian (little-endian) hardware is probably more important.



Further ASN.1 specifications


ASN.1 primitive types are 
    BOOLEAN, INTEGER, REAL, OID, OCTET STRING, ENUMERATED
 
Type constructors: SEQUENCE, SEQUENCE OF, SET, SET OF, CHOICE
 
 
Some ASN.1 structured data types  (not used for SNMP data, but used within SNMP MIB files for syntax definition)
 
1. PersonnelRecord on page 120 of Mani Subramanian, Network Management, 1999
(Note: could use SEQUENCE instead of SET; use of SET means data can be encoded in any order)
 
Note the numeric tags in square brackets, which get encoded in BER in the type bits. The data itself is then stored or transmitted with a tag field. This allows us to define CHOICE structures, as receiver can look at data tag to figure out which option it is.
 
    division CHOICE {
        marketing [0] SEQUENCE
          {Sector, State},
        research [1] CHOICE 
            {product [0] NULL,
             basic   [1] NULL},
        production [2] SEQUENCE
               {product-line, Country}
    }
     
    Trade-Message::= SEQUENCE  {
        invoice-no    INTEGER,
        name        GraphicString,
        details        SEQUENCE OF
                   SEQUENCE {partno INTEGER, quan INTEGER},
        cost        REAL
    }
     
    Trade-Message: uses SEQUENCE and SEQUENCE OF, also more basic types
         
            name GraphicString,
            details    SEQUENCE OF 
                   SEQUENCE {part-no INTEGER, quantity INTEGER},
            charge    REAL
             
The SEQUENCE OF creates an ARRAY of homogeneous records.
The SEQUENCE {part-no ...} creates a RECORD of two fields in a specified order. The two (or more) fields need not have the same type.
 
    details of SET/SEQUENCE
        what if we replaced SEQUENCE OF with SET OF above?
        could we replace SEQUENCE {...} with SET {...}??
 
    PageNum ::= INTEGER
    ChapNum ::= INTEGER
     
    BookPageNum ::= SEQUENCE {ChapNum, Separator, PageNum}
     
    BookPages ::= SEQUENCE OF {BookPageNum}
     
    What if we replace the above with
         BookPages ::= SET OF {BookPageNum}
      
 

 
 
BookPageNumber
BookPages
 
         
        structured types: BookPageNumber, etc
        
        BookPages::= SEQUENCE OF { BookPageNumber}
        or
            ::= SEQ. OF { SEQ { chapternumber, separator, pagenumber} }
             
        We could use SET OF instead of SEQUENCE OF, if we do not care about
        the order of the BookPages. However, we can't replace the inner SEQ
        with SET unless we have some way (perhaps tags?) of telling which
        is the chapternumber and which is the pagenumber. 
        
        more on tags
 


Accessing table as a tree: indexing is not needed
accessing using the index
 


 

MIB files

Case studies

AT group


The Address Translation group contains a single table, with three entries per row:

          atTable OBJECT-TYPE
              SYNTAX  SEQUENCE OF AtEntry
              ...
              ::= { at 1 }
 
          atEntry OBJECT-TYPE
              SYNTAX  AtEntry
              ACCESS  not-accessible
              STATUS  deprecated
              DESCRIPTION
                      "Each entry contains one NetworkAddress to
                      `physical' address equivalence."
              INDEX   { atIfIndex, atNetAddress }
              ::= { atTable 1 }
 
          AtEntry ::=
              SEQUENCE {
                  atIfIndex      INTEGER,
                  atPhysAddress  PhysAddress,
                  atNetAddress   NetworkAddress
              }
 
          atIfIndex OBJECT-TYPE
              SYNTAX  INTEGER
              ACCESS  read-write
              STATUS  deprecated
              DESCRIPTION
                      "The interface on which this entry's equivalence
                      is effective.  The interface identified by a
                      particular value of this index is the same
                      interface as identified by the same value of ifIndex."
              ::= { atEntry 1 }
 
          atPhysAddress OBJECT-TYPE
              SYNTAX  PhysAddress
              ACCESS  read-write
              STATUS  deprecated
              DESCRIPTION
                      "The media-dependent `physical' address. .."
              ::= { atEntry 2 }
 
          atNetAddress OBJECT-TYPE
              SYNTAX  NetworkAddress
              ACCESS  read-write
              STATUS  deprecated
              DESCRIPTION
                      "The NetworkAddress (e.g., the IP address)
                      corresponding to the media-dependent `physical'  address."
               ::= { atEntry 3 }

Look at the actual data, and how it is indexed. This could be called "sparse indexing", versus the "dense" indexing of the interfaces table. Note that the actual data from localhost is likely to be pretty limited; try the actual data from ulam3. Or try the localhost data after executing the pingsubnet script.