Network Management

Summer 2016, Corboy 201, TTh 5:30-8:45 pm

Class 4: July 14






Requirements checklist from Burke




20.10: Formal review of RFC1213 MIB2
    Network Types problem
   

20.11: Security
    VACM
    snmpwalk examples

20.12: SNMP and ASN.1 encoding
    TLV 4evah!

20.13: SNMPv2





A given SNMP request from a manager is identified by its community (also known as community string), which by default (and which will remain in our case to the irritation of network security people everywhere) the string "public". Actually, the string "public" corresponds to the "readonly" group, with access to (in our case) well nigh everything but we could change that.

Note that it is not possible to have two separate communities with the same community name, as the name is the lookup key. However, it is possible to configure net-snmp this way, in which case the given community string will receive the privileges of the first definition.

Here are the appropriate definitions from ulam3:/etc/snmp/snmpd.conf:

####
# 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    sysonly   default         public
com2sec    foosec    default         futhark
com2sec    all1361   default         tengwar

####
# Second, map the security names into group names; this mapping is indexed by (sec.name, sec.model)

#               sec.model  sec.name
group MyROFoo     v1         foosec
group MyROFoo     v2c        foosec
group MyRO1361    v1         all1361
group MyRO1361    v2c        all1361
group MyROSystem  v1         sysonly
group MyROSystem  v2c        sysonly
group MyROSystem  usm        sysonly

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

#           incl/excl   subtree                      mask
view all      included  .1                            80
view allinet  included  .1.3.6.1                      80
view system   included  .1.3.6.1.2.1.1
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 MyROFoo    ""      any       noauth    exact  fooview none  none
access MyRO1361   ""      any       noauth    exact  allinet none  none
# -----------------------------------------------------------------------------


All this reflects the View-based Access Control Method, or VACM: communities are tied to groups, which are what have the actual permissions. The second, third and fourth sections above correspond to:
Group names are part of the index of vacmAccessTable, and so will show up as OIDs rather than strings.

Note that we have two view statements, adding two specific subtrees to fooview. If we then do an snmpwalk to retrieve everything, using community "futhark", we get these two subtrees.

    snmpwalk -v 1 -c futhark 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   0xf4
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 0xf4 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.


The newer Net-SNMP  implementation simplifies this:

rocommunity public  default    -V systemonly
rocommunity  futhark  default   1.3.6.1.2.1
rocommunity  tengwar  default   -V mib2+private
rocommunity  galadriel default  1.3.6.1

view   systemonly  included   .1.3.6.1.2.1.1
view   systemonly  included   .1.3.6.1.2.1.25.1
view   systemonly  included   .1.3.6.1.2.1.2
view   systemonly  included   .1.3.6.1.2.2
view   systemonly  included   .1.3.6.1.2.1.4
view   systemonly  included   .1.3.6.1.2.1.6
view   mib2+private included  1.3.6.1.2.1
view   mib2+private included  1.3.6.1.4.1
 




ASN.1/BER syntax


See also luca.ntop.org/Teaching/Appunti/asn1.html.

SNMP uses Abstract Syntax Notation 1 (ASN.1) to define syntax; encoding into UDP packets is then done using the Basic Encoding Rules (BER). Right now it suffices to note the following:
  1. BER data is tagged with its type.
  2. ASN.1 and BER supports compound data types. However, SNMP does not use these; all data is atomic. (However, SNMP does support tables, which corresponds roughly to record formats and which can be pressed into service to represent, say, arrays.)
  3. SNMP uses the ASN.1 type constructors SEQUENCE (to define lists) and SEQUENCE OF (to define records). A table is a SEQUENCE of records; each record is a SEQUENCE OF a specific list of fields. (ASN.1 also has SET and SET OF constructors, but SNMP does not use these directly.)
  4. ASN.1 also supports CHOICE types, but these are used in SNMP only for very high-level definitions, eg
                      SimpleSyntax ::=
                          CHOICE {
                              number
                                  INTEGER,
                              string
                                  OCTET STRING,
                              object
                                  OBJECT IDENTIFIER,
                              empty
                                  NULL
                          }

  5. SNMP does not use all the available basic ASN.1/BER data types. In fact, only INTEGER, OCTET STRING, OBJECT IDENTIFIER, and NULL are allowed. However, some subtypes of INTEGER are created; these are called defined types.
  6. The BER encoding is such that the actual size in bytes of the data can be difficult to predict.

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)
   


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}
      
 

 
 
        structured types: BookPageNumber, etc
        
            BookPages::= SEQUENCE OF { PageNum }

        or
            BookPages ::= 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.