RFC 1155 - Section 3.2.1, p. 7: Four ASN.1 primitive types
INTEGER, OCTET STRING, OBJECT IDENTIFIER, and NULL.
RFC 1155 - Section 6, p. 18: IpAdrress is an IMPLICIT OCTET STRING (SIZE (4)).
RFC 1155 - Section 6, p. 18: NetworkAddress is an IpAddress.
RFC 1157 - Section 4.1.6, p. 26: An agent-addr is a NetworkAddress which is an IpAddress
ASN.1 BER represent an OCTET STRING as 0x04. The data type of the TRAP-PDU value agent-addr is 0x40 (seen in Wireshark).
Why is the data type of agent-addr not IMPLICIT OCTET STRING (SIZE (4))?
Four ASN.1 primitive types INTEGER, OCTET STRING, OBJECT IDENTIFIER, and NULL
They say that they picked just these types from the ASN.1 type system. These are are built-in, scalar ASN.1 data types. When it comes to BER serialization, these types have distinct IDs on the wire (AKA tags).
IpAdrress is an IMPLICIT OCTET STRING (SIZE (4))
ASN.1 lets you create new type from any other type by changing the tag and (optionally) imposing additional constraints on the value range. There are two ways to change the tag:
by overriding the tag of the base type (AKA IMPLICIT tagging)
by appending the new tag to the existing tag(s) of the base type (AKA EXPLICIT tagging)
So here they subtype the OCTET STRING type by replacing its tag by tag #0 in the APPLICATION name space. Arithmetically, this new tag would become 0 | 0x40.
Additionally, they enforce size constraint on the value range -- SIZE (4) e.g. values must have four octets.
NetworkAddress is an IpAddress.
An agent-addr is a NetworkAddress which is an IpAddress
Well, yes and no. ;-)
ASN.1 has a CHOICE pseudo type which is invisible on the wire (does not have its own tag by default) but on the wire it can turn into one of the types it "embeds". The designers were probably planning to support more different network addresses in the future so they wrapped IpAddress by the NetworkAddress container.
ASN.1 BER represent an OCTET STRING as 0x04. The data type of the TRAP-PDU value agent-addr is 0x40 (seen in Wireshark).
That's correct: you see a subtype of OCTET STRING having IMPLICIT APPLICATION 0 tag which is 0x40 | 0.
BTW, with the IMPLICIT tagging, the original tag of the type is lost so do not see any remnants of the base OCTET STRING type on the wire.
Why is the data type of agent-addr not IMPLICIT OCTET STRING (SIZE (4))?
It actually is! Hope the above explanation makes it a little less obscure. ;-)
Related
RFC1155, 3.2.3.2 defines an IpAddress as:
This application-wide type represents a 32-bit internet address. It
is represented as an OCTET STRING of length 4, in network byte-order.
And again, in section 6
IpAddress ::=
[APPLICATION 0] -- in network-byte order
IMPLICIT OCTET STRING (SIZE (4))
Also RFC 1156, 5.4.1 defines an ipAddrEntry as a SEQUENCE containing an ipAdEntAddr as an IpAddress.
Okay then, backpedaling through this "(NOT)Simple Network Message Protocol", it should be clear that an IPAddress is 4 byte, network byte order (Big Endian)
RFC1156, 5.4.1 defines an ipAddrEntry to be a SEQUENCE containing among other things, ipAdEntAddr as an IpAddress... Which was defined in the last RFC.
Why then, in Wireshark, when I examine the OID request, the IP address 192.168.1.21 is encoded as
81 40 81 28 01 15
Which isn't 4 octets. It's not even TLV encoding. It's some other 7 bit encoding where the 8th bit is used to indicate "Not the last octet" (I think it's VLQ? )
1000 0001 0100 0000 => 1100 0000
1000 0001 0010 1000 => 1010 1000
If it is supposed to be VLQ, where is that documented?
Why is what I read on Wireshark not what I read in the RFCs?
SNMP protocol and Wireshark are both correct, but you misunderstand the basic concept of SNMP table and OIDs.
While from the text, you might see 192.168.1.21 anywhere in Wireshark's decoded tree view, you must be aware of its actual entity so as to understand how it was encoded.
In the screen shot you can see that 192.168.1.21 highlighted was part of an OID 1.3.6.1.2.1.4.20.1.2.192.168.1.21. This is a typical OID pattern used in tables, where the index 192.168.1.21 is combined with the table column OID 1.3.6.1.2.1.4.20.1.2 to represent the actual cell.
Thus, its bytes are encoded following BER like any other OIDs, not IpAddress,
How does ASN.1 encode an object identifier?
That's why the bytes look different from your initial thought.
you may remember my last answer, in which I explained that every instance of an object has a "name" and an INDEX. I referenced ifTable to show how ifIndex was used to refer to each instance of a row. While many (if not most) SNMP tables use a single INTEGER as the INDEX, it's actually possible to use any combination of primitive data types (there are tables that include an OID in the index, giving you an OID within an OID). The important thing is that the INDEX be unique for each row.
Based on the OID in your screenshot, it looks like you are dealing with ipAdEntIfIndex (from RFC 1213's IP-MIB). If you consult the MIB, you will find this definition for ipAddrEntry (representing a row in ipAddrTable):
ipAddrEntry OBJECT-TYPE
SYNTAX IpAddrEntry
MAX-ACCESS not-accessible
STATUS deprecated
DESCRIPTION
"The addressing information for one of this entity's IPv4
addresses."
INDEX { ipAdEntAddr }
::= { ipAddrTable 1 }
IpAddrEntry ::= SEQUENCE {
ipAdEntAddr IpAddress,
ipAdEntIfIndex INTEGER,
ipAdEntNetMask IpAddress,
ipAdEntBcastAddr INTEGER,
ipAdEntReasmMaxSize INTEGER
}
You can see here that it uses ipAdEntAddr as the INDEX, and that ipAdEntAddr is an IpAddress. The reason that the encoding in your screenshot looks funny is that it's encoded as part of an OID, not as an OctetString, as an IpAddress would normally be.
There are two different specifications that are relevant here. The first is the specification for the ASN.1 Basic Encoding Rules, which is not publicly available for free, but basically says that OID sub-identifiers can only use the lower 7 bits of each byte, and that the MSB is a flag indicating whether there are additional bytes in that sub-identifier. Hence the 0x01 in 0x81 gives bit 7, and 0x40 gives bits 0-6. If you put them together, you get 0xc0, which is 192.
The second specification is RFC 2578 (section 7.7), which defines how an INDEX is encoded within an OID:
The syntax of the objects in the INDEX clause indicate how to form
the instance-identifier:
(1) integer-valued (i.e., having INTEGER as its underlying primitive
type): a single sub-identifier taking the integer value (this
works only for non-negative integers);
(2) string-valued, fixed-length strings (or variable-length preceded by
the IMPLIED keyword): `n' sub-identifiers, where `n' is the length
of the string (each octet of the string is encoded in a separate
sub-identifier);
(3) string-valued, variable-length strings (not preceded by the IMPLIED
keyword): `n+1' sub-identifiers, where `n' is the length of the
string (the first sub-identifier is `n' itself, following this,
each octet of the string is encoded in a separate sub-identifier);
(4) object identifier-valued (when preceded by the IMPLIED keyword):
`n' sub-identifiers, where `n' is the number of sub-identifiers in
the value (each sub-identifier of the value is copied into a
separate sub-identifier);
(5) object identifier-valued (when not preceded by the IMPLIED
keyword): `n+1' sub-identifiers, where `n' is the number of sub-
identifiers in the value (the first sub-identifier is `n' itself,
following this, each sub-identifier in the value is copied);
(6) IpAddress-valued: 4 sub-identifiers, in the familiar a.b.c.d
notation.
An IpAddress is simple, because it's represented as dot-separated integers, just like an OID.
I need clarity related MAC SNMP oid .1.3.6.1.2.1.2.2.1.6.
The oid is retrieving data in two formats - octet string and hex string. Ex:
1) octet string .1.3.6.1.2.1.2.2.1.6.2 STRING: 00:01:80:5c:df:1c
2) hex string. .1.3.6.1.2.1.2.2.1.6.1 STRING: 30:30:3a:30:30:3a:30:30:3a:30:30:3a:30:30:3a:30:30
Hence while writing code logic, we need to handle both variety. But How to identify the retrieving data is octet string or hex string? Is this related to little endian or big endian concept? Any OID will provide us inform type of data. Kindly help.
This is specified in the relevant MIB. You should work with MIBs, not just numerical OIDs out of context. An OID just tells you which object is being referred to, but there is so much more information in the MIB about those objects, including their type and how they ought to be interpreted.
In fact there is no such thing as a HEX STRING; these are usually OCTET STRINGs with a display-hint that they should be shown to the user in hex-pair notation.
OCTET STRINGs don't have endianness; they're strings. Sequences.
.proto examples all seem to start numbering their fields at one.
e.g. https://developers.google.com/protocol-buffers/docs/proto#simple
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
If zero can be used, it will make some messages one or more bytes smaller (i.e. those with a one or more field numbers of 16).
As the key is simply a varint encoding of (fieldnum << 3 | fieldtype) I can't immediately see why zero shouldn't be used.
Is there a reason for not starting the field numbering at zero?
One very immediate reason is that zero field numbers are rejected by protoc:
test.proto:2:28: Field numbers must be positive integers.
As to why Protocol Buffers has been designed this way, I can only guess. One nice consequence of this is that a message full of zeros will be detected as invalid. It can also be used to indicate "no field" internally as a return value in protocol buffers implementation.
Assigning Tags
As you can see, each field in the message definition has a unique numbered tag. These tags are used to identify your fields in the message binary format, and should not be changed once your message type is in use. Note that tags with values in the range 1 through 15 take one byte to encode, including the identifying number and the field's type (you can find out more about this in Protocol Buffer Encoding). Tags in the range 16 through 2047 take two bytes. So you should reserve the tags 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.
The smallest tag number you can specify is 1, and the largest is 229-1, or 536,870,911. You also cannot use the numbers 19000 through 19999 (FieldDescriptor::kFirstReservedNumber through FieldDescriptor::kLastReservedNumber), as they are reserved for the Protocol Buffers implementation - the protocol buffer compiler will complain if you use one of these reserved numbers in your .proto. Similarly, you cannot use any previously reserved tags.
https://developers.google.com/protocol-buffers/docs/proto
Just like the document says, 0 can't be detected.
I'm using SNMP4J to read info of devices with SNMP. Now I found some devices which represent the system name (OID iso.3.6.1.2.1.1.5.0) as a Hex-STRING instead of a STRING.
To show the system name I use the following code:
Variable var = response.getVariable(new OID(".1.3.6.1.2.1.1.5.0"));
System.out.println(var.toString());
Where response is a PDU object.
If the system name is represented as a STRING value, this goes as I expected. When it is represented as a Hex-STRING, it just prints the Hex value.
Example:
Take the name of the system as "SYSTEM NAME".
With STRING it prints "SYSTEM NAME".
With Hex-STRING it prints "53:59:53:54:45:4d:20:4e:41:4d:45"
Now with snmpwalk in command line I can just use the -Oa flag. This makes all Hex-STRING values show as STRING. Is it possible to use this flag in SNMP4J or is there a similar option?
I'm not sure where you're getting the term "Hex-STRING" from. SNMP does not define such a data type. I suggest you read through the relevant RFC documents, they are publicly available from IETF. The wikipedia article for SNMP (http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol#References) has an excellent reference list, you can start with browsing the ones marked as "STD".
In SNMP, all strings are subtypes (or in a different word, "restrictions") of OCTET-STRING, a byte string of indeterminate length. It may contain any data, even non-printable stuff, representing a jpeg image or whatever.
Some textual-conventions have been defined, which restrict the data to some specific byte range, or length. A DisplayString is defined to only contain bytes from the NVT ASCII character set, so the user may trust it to be printable.
In fact, sysName is defined to be a DisplayString with a max length of 255 characters.
sysName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
Since a good SNMP manager is aware of RFC1213-MIB, which defines both sysName and DisplayString, the manager should assume that the data received is printable ASCII characters.
When you say "When it is represented as a Hex-STRING", what do you mean? "Represented" where, on the agent or in your Java code or when using the net-snmp "snmpwalk" command?
The var.toString() call should convert the contents of the variable into something that could be safely printed in a terminal, so it's possible that SNMP4j is converting any binary string to a hex string.
I find type byte byte very confusing in byte godoc , isn’t supposed to be type byte uint8 ?
byte is an alias for uint8 and is equivalent to uint8 in all ways. It
is used, by convention, to distinguish byte values from 8-bit unsigned
integer values. type complex128
There is no real package builtin. But to explain the builtins a synthetic package builtin with synthetic types is generated for godoc. The type byte is never realy declared as type byte byte simply because byte is builtin and doesn't need a declaration. (And no byte is not and should not be declared as type byte uint8: byte is bultin and not declared.)
Just ignore the synthetic declarations and read the description. The description is the useful stuff here.
I think the answer is in the presentation of the package:
The items documented here are not actually in package builtin but their descriptions here allow godoc to present documentation for the language's special identifiers.
This syntax has no real meaning, it is just a sort of placeholder for having the type byte being documented like other (non built-in) types. byte is defined internally, and the line type byte byte is never actually encountered. Being internal, it would not be defined as a 'regular' alias of uint8, which would indeed be type newbyte uint8 (although type newbyte byte would be as valid and probably clearer).