英文:
What is the name 0x41 (65) in a snmp variable bindings reply?
问题
我正在尝试理解SNMP(通常指v3)。目标是在运行实时操作系统(RTOS)的嵌入式设备中包括一个SNMP代理。
我已经浏览了超过十几个RFC,至少还有十几个要读。每一个都引发了更多问题,而不是回答问题。(1052, 1065, 1067, 1155, 1156, 1157, 1212, 1213, 1592, 1905, 2578, 2579, 2580, 3410, 3411, 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3584... )
我曾经用几个RFC实现了mDNS-SD和802.1X EAPOL,但这并没有那么令人困惑。
我考虑过的许多书的评论都抱怨了材料的不一致和模糊性。我买了一些评论更好的书。
在线搜索没有取得进展,主要是因为关键词没有找到我想要的答案。所以我甚至可能不知道最好的搜索关键词。
最终,我决定尝试反向工程正在发生的事情,我在Linux PC上安装了WireShark以及snmpd和snmp工具,以便进行嗅探。以下是我拥有的内容,以及我无法将所看到的与所读的内容对齐。
这是v3嗅探,这是对管理者的第一个请求的回复。这个问题只是聚焦在我想要了解的其中一件事上。我无法解码和检查明文PDU,因为我无法获得v2或v1的请求。
Wireshark显示这是对管理者的回复。这显然是无论将使用什么身份验证的第一步。
我拥有的书显示这是线上的协议。我正在尝试解析可变绑定。
这里是来自Wireshark的可变绑定。
一个长度为15字节的"sequence"(x30 x0f)
从RFC中来看,这个列表是VarBinds的SEQUENCE,其中每个VarBind都包括对象名称和ObjectSyntax中的值。到目前为止,一切看起来都还好。
以下是SEQUENCE中的下一部分(Wireshark突出显示了所有14字节)
一个长度为10字节的对象ID(x06, x0a)
这是实际的对象:
对象名称是对象ID,它是x2b x6 x1 x6 x3 xf x1 x1 xx4 x0,或者(1.3).6.1.6.3.15.1.1.4.0
鉴于这是ISO、ORG、DOD、INTERNET、6?... 我必须假设“6”是互联网分支下的一个对象,我还没有遇到过。可能与v3安全有关。
接下来是值。
这是一个类型为x41(65),长度为1,值为7。
那么,在“ObjectSyntax”中,x41是什么?我找不到它的定义。事实上,所有这些RFC都使用标识符的单词,而我只能找到它们实际数字值的一小部分。
Wireshark知道它是什么... 它说是“Counter32”... 这是x41应该是什么吗?如果是这样,它根本不是32位。它只有一个字节。我还想找到它的定义。
此外,某处(我甚至不能回想起哪个RFC)说对OID请求的回复是将值附加到所请求的对象,而不是替换零(例如:请求:1.3.6.1.4.300.1 -> 回复 1.3.6.1.4.300.1.15,所以它是一个值为15)。这个OID有一个尾随的零,我不确定为什么。
有人可以指导我找到一些有用、简洁、浓缩的信息来解释这个材料吗?每个RFC都要求我回头阅读一些以前(有时是过时的)的RFC,我现在已经有了超过25个。我不认为编写一个“简单”的SNMP代理需要这么多的RFC。一个月的研究,我所拥有的大部分东西都是如何阅读MIB文件。虽然这也需要一些脑力体操。
“简单”相当具有欺骗性(正如多位书评人所说)。
英文:
I am attempting to understand SNMP (in general, and v3). The goal is to include an snmp agent in an embedded device running an RTOS.
I've already been through over a dozen RFCs with at least another dozen more to go. Each one creates more questions than it answers. (1052, 1065, 1067, 1155, 1156, 1157, 1212, 1213, 1592, 1905, 2578, 2579, 2580, 3410, 3411, 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3584... )
I implemented mDNS-SD and 802.1X EAPOL with just a couple RFCs and it wasn't this confusing.
Many of the reviews of books I considered all complain of the same inconsistent and vagueness of the material. I bought a couple books that had better reviews.
Searching online isn't getting anywhere largely because the keywords aren't finding things I want answers to. So I must not even know the best keywords to search with.
Eventually, I decided to just try to reverse engineer what's going on, I installed WireShark on a Linux PC, and the snmpd and snmp tools, so I could sniff it. Here is what I have, and can't align what I see with what I read.
This is a v3 sniff, It's a reply to the first request from a manager. This question is just zeroing in on one of the things that I want to understand. I can't decode and examine a plaintext PDU, because I can't get a request in v2 or v1.
Wireshark shows this reply to a manager. It's apparently the first step in whatever authentication it to be used.
The book I have shows this as the protocol on the wire. And I am trying to parse out the variable bindings.
Here are the variable bindings from Wireshark
A "sequence" that is 15 bytes long (x30 x0f)
This, from the RFC, says that the list is a SEQUENCE of VarBinds, where each VarBind is the object name, and the value in ObjectSyntax. So it's looking okay so far.
Here is the next segment inside the SEQUENCE (Wireshark highlighted all 14 bytes)
An object ID that is 10 bytes long (x06, x0a)
Here is the actual object:
The objectName is the object ID, and it is x2b x6 x1 x6 x3 xf x1 x1 xx4 x0 or (1.3).6.1.6.3.15.1.1.4.0
Given that this is ISO, ORG, DOD, INTERNET, 6?... I have to assume "6" is an object under internet branch I've not yet come across. Likely something to do with the v3 security.
Next, is the value.
This is a type x41 (65), with a length of 1, and a value of 7.
Well, in "ObjectSyntax" what is x41? I can't find it defined anywhere.
For that matter, all these RFCs use words for identifiers, and I can find only a fraction of what their actual numeric values are.
Wireshark knew what it was... It's saying "Counter32"... is that what x41 is supposed to be? If so, it's nowhere near 32 bits. It's only one byte. Again, I'd like to find it's definition.
Also, somewhere, (I can't even recall which RFC) it said the reply to an OID request is to append the value to the requested object, not replace the zero (example: request: 1.3.6.1.4.300.1 -> reply 1.3.6.1.4.300.1.15 so it is a value of 15 ). This OID has a trailing zero, nad I'm not sure why.
Can anyone point me to some useful, concise, condensed information explaining this material? Every RFC requires that I go back and read some previous (and sometimes obsoleted) RFC, and I've now got over 25 of them already. I don't think it should take this many RFCs to be able to write an "simple" snmp agent. A month of researching, and most of what I have to show for it is how to read MIB files. Although that take some mental gymnastics too.
"Simple" is rather deceptive (as more than one book reviewer has stated).
答案1
得分: 1
RFC 1157规定了SNMP消息使用“ASN.1的基本编码规则的子集”进行编码。我认为官方的基本编码规则(BER)规范并不免费提供,但在网上可以很容易找到解释(这里是我找到的一个简单搜索的结果)。关于0x41
字节的问题,这是一个BER标识符。最高位的两位(01)告诉你“类别”(类似于命名空间)是“应用程序”。 “表单”位(0)告诉你它是一个原始类型(即不是一个序列)。最后,“标签”是1。参考SNMPv2-SMI
MIB(RFC 2578)你可以找到这个定义:
Counter32 ::=
[APPLICATION 1]
IMPLICIT INTEGER (0..4294967295)
你还问到为什么32位整数只用一个字节进行编码。这需要你区分SNMP标准和ASN.1标准的范围。ASN.1只有一个INTEGER类型,它1)具有无限范围,2)总是有符号的(二进制补码),3)应该以尽可能少的八位字节进行编码。实际上,这意味着Counter32
(或任何其他32位无符号整数类型)的编码可能会使用多达5个字节(请参见我对此问题的这个答案)。
最后,你问到答复是如何修改所请求的OID的。我曾经对此感到困惑了很长时间,但当我弄清楚时,我意识到这实际上非常简单。我认为最好的起点是RFC 1157中的这段摘录:
在SNMP操作中,MIB中定义的任何对象类型的每个实例都由一个称为其“变量名称”的唯一名称来标识。一般来说,SNMP变量的名称是一个形式为x.y的OBJECT IDENTIFIER,其中x是MIB中定义的非聚合对象类型的名称,y是一个OBJECT IDENTIFIER片段,以一种特定于所命名的对象类型的方式,标识所需的实例。
这种命名策略充分利用了GetNextRequest-PDU的语义(参见第4节),因为它分配了与MIB中已知的所有变量名称在词典顺序中连续的相关变量的名称。
针对许多类别的对象类型,为对象实例的类型特定命名在下面定义。不适用以下命名约定的对象类型的实例由MIB定义中所述的对象标识符x.0命名。
例如,假设要标识sysDescr变量的一个实例。sysDescr的对象类是:
iso org dod internet mgmt mib system sysDescr
1 3 6 1 2 1 1 1
因此,对象类型x将是1.3.6.1.2.1.1.1,其中附加了一个实例子标识符为0。也就是说,1.3.6.1.2.1.1.1.0标识了sysDescr的唯一实例。
因此,总结一下,来自MIB的OID并不是指具体的对象,而是“对象类型”。每个具体对象(即“实例”)都由一个或多个子标识符的后缀(即此解释中的y
)来标识。对于单例对象,此后缀始终为0
。然而,我认为大多数SNMP对象都是在表格中找到的,而不是在单例对象中。实际上,我不知道有关此在标准中的良好解释,因此我将尽我所能给出解释。
与任何表格一样,SNMP表格由行和列组成。但在SNMP中,行称为“条目”,每个条目定义了一个自定义类型来描述列。以下是来自IF-MIB
的一个简单示例:
ifTable OBJECT-TYPE
SYNTAX SEQUENCE OF IfEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A list of interface entries. The number of entries is
given by the value of ifNumber."
::= { interfaces 2 }
ifEntry OBJECT-TYPE
SYNTAX IfEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry containing management information applicable to a
particular interface."
INDEX { ifIndex }
::= { ifTable 1 }
IfEntry ::=
SEQUENCE {
ifIndex InterfaceIndex,
ifDescr DisplayString,
ifType IANAifType,
ifMtu Integer32,
ifSpeed Gauge32,
ifPhysAddress PhysAddress,
ifAdminStatus INTEGER,
ifOperStatus INTEGER,
ifLastChange TimeTicks,
ifInOctets Counter32,
ifInUcastPkts Counter32,
ifInNUcastPkts Counter32, -- deprecated
ifInDiscards Counter32,
ifInErrors Counter32,
ifInUnknownProtos Counter32,
ifOutOctets Counter32,
ifOutUcastPkts Counter32,
ifOutNUcastPkts Counter32, -- deprecated
ifOutDiscards Counter32,
ifOutErrors Counter32,
ifOutQLen Gauge32, -- deprecated
ifSpecific OBJECT IDENTIFIER -- deprecated
}
所以,ifTable
的OID是1.3.6.1.2.1.2.2
,而ifEntry
的OID是1.3.6.1.2.1.2.2.1
。IfEntry
中的每个项目也有自己的定义,其中包括相对于ifEntry
的OID。通常它们与条目的数据类型相匹配,因此例如,
英文:
RFC 1157 specifies that SNMP messages are encoded with "a subset of the basic encoding rules of ASN.1". I don't think the official basic encoding rules (BER) specification is available for free, but it's not hard to find explainers online (here's one I found with a simple search). To your question about the 0x41
byte, this is a BER identifier. The 2 most-significant bits (01) tell you the "class" (i.e. something like a namespace) is "application". The "form" bit (0) tells you that it's a primitive type (i.e. not a sequence). Finally the "tag" is 1. Consulting the SNMPv2-SMI
MIB (RFC 2578) you can find this definition:
Counter32 ::=
[APPLICATION 1]
IMPLICIT INTEGER (0..4294967295)
You also asked about why a 32-bit integer is encoded with a single byte. This requires you to distinguish between the scope of the SNMP standard versus the ASN.1 standard. ASN.1 only has a single INTEGER type, which 1) has an unlimited range, 2) is always signed (two's complement), and 3) should be encoded in the least number of octets possible. This actually means that a Counter32
(or any other 32-bit unsigned integer type) might use up to 5 bytes for its encoding (see this answer I gave to a question about that).
Finally, you asked about the way the replies are modifying the requested OID. I was confused about this for a long time, but when I figured it out, I realized it's actually pretty simple. I think the best place to start is with this excerpt from RFC 1157:
Each instance of any object type defined in the MIB is identified in
SNMP operations by a unique name called its "variable name." In
general, the name of an SNMP variable is an OBJECT IDENTIFIER of the
form x.y, where x is the name of a non-aggregate object type defined
in the MIB and y is an OBJECT IDENTIFIER fragment that, in a way
specific to the named object type, identifies the desired instance.
This naming strategy admits the fullest exploitation of the semantics
of the GetNextRequest-PDU (see Section 4), because it assigns names
for related variables so as to be contiguous in the lexicographical
ordering of all variable names known in the MIB.
The type-specific naming of object instances is defined below for a
number of classes of object types. Instances of an object type to
which none of the following naming conventions are applicable are
named by OBJECT IDENTIFIERs of the form x.0, where x is the name of
said object type in the MIB definition.
For example, suppose one wanted to identify an instance of the
variable sysDescr The object class for sysDescr is:
iso org dod internet mgmt mib system sysDescr
1 3 6 1 2 1 1 1
Hence, the object type, x, would be 1.3.6.1.2.1.1.1 to which is
appended an instance sub-identifier of 0. That is, 1.3.6.1.2.1.1.1.0
identifies the one and only instance of sysDescr.
So, to summarize, the OID that comes from the MIB doesn't refer to a concrete object, but to the "object type". Each concrete object (i.e. "instance") is identified by a suffix of one or more sub-identifiers (i.e. the y
in this explanation). For singleton objects, this suffix is always 0
. However, I think most SNMP objects are found in tables, not in singleton objects. I don't actually know of a good explanation of this in the standards, so I'll give it my best shot.
Like any table, SNMP tables are made up of rows and columns. In SNMP, however, the rows are called "entries", and each entry defines a custom type to describe the columns. Here's a simple example from the IF-MIB
:
ifTable OBJECT-TYPE
SYNTAX SEQUENCE OF IfEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A list of interface entries. The number of entries is
given by the value of ifNumber."
::= { interfaces 2 }
ifEntry OBJECT-TYPE
SYNTAX IfEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry containing management information applicable to a
particular interface."
INDEX { ifIndex }
::= { ifTable 1 }
IfEntry ::=
SEQUENCE {
ifIndex InterfaceIndex,
ifDescr DisplayString,
ifType IANAifType,
ifMtu Integer32,
ifSpeed Gauge32,
ifPhysAddress PhysAddress,
ifAdminStatus INTEGER,
ifOperStatus INTEGER,
ifLastChange TimeTicks,
ifInOctets Counter32,
ifInUcastPkts Counter32,
ifInNUcastPkts Counter32, -- deprecated
ifInDiscards Counter32,
ifInErrors Counter32,
ifInUnknownProtos Counter32,
ifOutOctets Counter32,
ifOutUcastPkts Counter32,
ifOutNUcastPkts Counter32, -- deprecated
ifOutDiscards Counter32,
ifOutErrors Counter32,
ifOutQLen Gauge32, -- deprecated
ifSpecific OBJECT IDENTIFIER -- deprecated
}
So, ifTable
has an OID of 1.3.6.1.2.1.2.2
, and ifEntry
has an OID of 1.3.6.1.2.1.2.2.1
. Each item in IfEntry
also has its own definition, which includes the OID relative to ifEntry
. Generally they match up with the entry's data type, so, for example, ifIndex
, as the first column in IfEntry
, has an OID of ifEntry.1
. Confusingly, when you do a simple Get-Next walk, you will traverse in column-major order, meaning you will get all the ifIndex
es, followed by all the ifDescr
s, and so on.
So, with all that explained, I'm now prepared to explain the instance identifiers for these tables. Notice above that ifEntry
defines
INDEX { ifIndex }
This means, first, that each row is guaranteed to have a unique ifIndex
, and, more importantly, that the ifIndex
is used as the instance identifier for the entire entry. For example, you can pick any column in the IfEntry
data type, let's say ifOperStatus
(1.3.6.1.2.1.2.2.1.8), and use Get-Next to find the first instance of that column. Let's say its OID is 1.3.6.1.2.1.2.2.1.8.1, and it's value is 1
(up). The last sub-identifier tells you that it belongs to the row whose ifIndex
is 1
. To find the name of that interface, you can then query ifDescr.1
, and to find its speed setting, you can query ifSpeed.1
, and so forth. In this case, it is possible to query ifIndex.1
, which will just return 1
, but in many tables, the INDEX
columns are not-accessible
, meaning you can only find out what instances there are by walking some other column. Some tables also use multiple indices, or use OCTET STRING
or even OBJECT IDENTIFIER
rather than INTEGER
typed indices. The rules for encoding and decoding those are in RFC 2578 section 7.7.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论