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:
- vacmSecurityToGroupTable
- vacmViewTreeFamilyTable
- vacmAccessTable
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:
- BER data is tagged with its
type.
- 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.)
- 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.)
- 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
}
- 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.
- 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:
This is applied recursively, as necessary.
The TAG identifies Type: basic ASN.1 type, class, etc
Bits:
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:

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.