背景
学c++想通过源码来熟悉,朋友推荐SNMP,顺便记录下学习过程。
SNMP 协议标准
参考 www.net-snmp.org/ 里面有如下SNMP介绍
SNMP is a widely used protocol for monitoring the health and welfare of network equipment (eg. routers), computer equipment and even devices like UPSs. Net-SNMP is a suite of applications used to implement SNMP v1, SNMP v2c and SNMP v3 using both IPv4 and IPv6
这里面提了三个版本
- v1 RFC 1157
- v2 RFC 1901
- v3 RFC 2571
snmp v1版本
协议地址
www.rfc-editor.org/rfc/rfc1157...
之后的基于谷歌翻译进行整理
1协议状态
本备忘录,逻辑远程用户可以检查或更改网络元素的管理信息。
这些文档描述管理信息结构和配套备忘录一起为管理基于 TCP/IP 的互联网
(尤其是互联网)提供了一种简单、可行的架构和系统
2协议简介
yaml
IAB 建议制定互联网网络管理标准,这是一项双管齐下的战略对基于 TCP/IP 的互联网进行网络管理。
短期内,简单网络管理协议(SNMP)将成为用于管理互联网社区中的节点。
制定了两个文档来定义管理信息:
RFC 1065,定义了管理信息结构(SMI)以及
RFC 1066,定义了管理信息库(MIB)
IAB 已指定 SNMP、SMI 和初始 Internet MIB 给完全符合"标准协议"且状态为"推荐"。
通过此行动,IAB建议所有IP和TCP实现都网络可管理,
并且网络的实现可管理的预计将采用和实施 SMI、MIB 和SNMP。
当前基于 TCP/IP 的网络管理框架互联网包括:
管理结构和识别基于 TCP/IP 的互联网信息,描述了如何管理MIB 中包含的对象定义在RFC 1155中;
TCP/IP 网络管理的管理信息库基于互联网,它描述了包含在RFC 1156中规定的 MIB ;
以及简单网络管理协议,定义用于管理这些对象的协议,如本备忘录中阐明
IAB 建议制定互联网网络管理规范、互联网活动董事会已指示互联网工程任务组 (IETF)
在网络管理领域建立两个新的工作组。
一个小组负责进一步规范和定义管理信息库 (MIB) 中要包含的元素。
另一人负责定义对简单网络管理协议(SNMP)以适应短期网络供应商和运营社区的需求,并协调
与 MIB 工作组的输出一致
MIB 工作组发布了两份备忘录,其中一份定义了管理信息结构 (SMI)供管理 MIB 中包含的对象。
第二份备忘录定义了管理对象
SNMP 扩展工作组的成果是本备忘录,其中纳入了对初始 SNMP 定义的更改,需要与 MIB 工作组的输出保持一致
3 体系结构
3体系结构
scss
SNMP 体系结构模型中隐含着网络管理站和网络元素的集合。
网络管理站执行管理应用程序来监视和控制网络元素。
网络元素是诸如主机、网关、终端服务器等设备,
它们具有管理代理,负责执行网络管理站请求的网络管理功能。
简单网络管理协议 (SNMP) 用于在网络管理站和网络元素中的代理之间传递管理信息
3.2.4协议交换的形式和含义
在 SNMP 中,管理实体之间的管理信息通信这些消息的形式和含义在下面的第 4 节中定义。
与最小化管理代理复杂性的目标一致,SNMP 消息的交换只需要不可靠的数据报服务,
并且每条消息都完全且独立地由单个传输数据报表示。
虽然本文档指定了通过 UDP 协议的消息交换,但SNMP 的机制通常适用于各种传输服务
3.2.5管理关系的定义
sql
体系结构允许参与协议的实体之间存在各种管理
驻留在管理站和网络元素上使用 SNMP 相互通信的实体称为 SNMP应用程序实体。
实现 SNMP 并因此支持 SNMP 应用程序实体的对等进程称为协议实体
SNMP 代理与任意一组 SNMP 应用程序实体的配对称为 SNMP 社区。
每个 SNMP社区都由一个八位字节字符串命名,该字符串称为该社区的社区名称。
由 SNMP 应用程序实体发起的 SNMP 消息
实际上属于由该消息的社区组件命名的 SNMP 社区,该消息称为真实 SNMP 消息。
将 SNMP 消息识别为特定 SNMP 社区的真实 SNMP 消息的称为身份验证方案。
根据一个或多个身份验证方案识别真实 SNMP 消息的功能的实现身份验证服务
对于任何网络元素,MIB 中与该元素相关的对象子集称为 SNMP MIB 视图
集合 { READ-ONLY, READ-WRITE } 的元素称为 SNMP访问模式
访问模式与 SNMP MIB 视图的配对称为SNMP 社区配置文件
SNMP 社区配置文件表示对指定 MIB 视图中变量的指定访问权限
社区与 SNMP 社区配置文件的配对称为SNMP 访问策略。
访问策略表示由指定 SNMP 社区的 SNMP 代理向该社区的其他成员提供的指定社区配置文件
SNMP应用程序实体之间的所有管理关系都是根据 SNMP 访问策略在架构上定义的
对于每个 SNMP 访问策略,
如果指定 SNMP 社区的 SNMP 代理所在的网络元素不是指定配置文件的 MIB 视图所属的网络元素,
则该策略称为 SNMP 代理访问策略
与代理访问策略关联的 SNMP 代理称为 SNMP 代理代理
4协议规范
scss
网络管理协议是一种应用协议,通过它可以检查或更改代理的 MIB 变量。
协议实体之间的通信是通过交换消息来实现的使用 ASN.1 的基本编码规则
在单个 UDP 数据报中完全独立地表示(如第 3.2.2 节所述)。
消息由版本标识符、SNMP 社区名称和协议数据单元 (PDU) 组成。
关联的主机上的 UDP 端口 161 上接收traps的消息之外的所有消息
(即除包含 Trap-PDU 的消息之外的所有消息)。
报告traps的消息应在 UDP 端口 162 上接收以进行进一步处理
所有 SNMP 实现都必须支持这五个PDU:
GetRequest-PDU,
GetNextRequest-PDU,
GetResponse-PDU,
SetRequest-PDU,
Trap-PDU
4.1 定义
sql
RFC1157-SNMP DEFINITIONS ::= BEGIN
IMPORTS
ObjectName, ObjectSyntax, NetworkAddress, IpAddress, TimeTicks
FROM RFC1155-SMI;
-- top-level message
Message ::=
SEQUENCE {
version -- version-1 for this RFC
INTEGER {
version-1(0)
},
community -- community name
OCTET STRING,
data -- e.g., PDUs if trivial
ANY -- authentication is being used
}
-- protocol data units
PDUs ::=
CHOICE {
get-request
GetRequest-PDU,
get-next-request
GetNextRequest-PDU,
get-response
GetResponse-PDU,
set-request
SetRequest-PDU,
trap
Trap-PDU
}
-- the individual PDUs and commonly used
-- data types will be defined later
END
这里使用了一种 ASN.1 的基本编码规则,使用ds能查到相关的规则
ASN.1(Abstract Syntax Notation One,抽象语法表示法)
是一种用于描述数据结构和编码规则的国际标准(由 ITU-T X.680 和 X.690 定义), 广泛应用于网络协议(如 SNMP、LDAP、X.509 证书)中。
以下是其核心语法和编码规则的详细说明: 一、ASN.1 语法
- 基本数据类型 ASN.1 定义了多种基础数据类型,以下是常见类型:
类型 | 描述 | 示例(ASN.1 定义) |
---|---|---|
INTEGER | 整数 | counter INTEGER ::= 42 |
BOOLEAN | 布尔值(TRUE/FALSE) | enabled BOOLEAN ::= TRUE |
OCTET STRING | 字节序列(二进制或字符串) | name OCTET STRING ::= "DeviceA" |
NULL | 空值 | empty NULL ::= NULL |
OBJECT IDENTIFIER | 全局唯一标识符(OID) | sysDescr OID ::= { 1.3.6.1.2.1.1.1 } |
SEQUENCE | 有序结构体(类似结构体) | Person ::= SEQUENCE {<br> name OCTET STRING,<br> age INTEGER } |
SEQUENCE OF | 同类型元素的列表 | PortList ::= SEQUENCE OF INTEGER |
CHOICE | 多选一的结构 | Value ::= CHOICE {<br> intVal INTEGER,<br> strVal OCTET STRING } |
- 模块定义 ASN.1 通过模块(
Module
)组织数据结构的定义,语法如下:
asn.1
ModuleName DEFINITIONS ::= BEGIN
-- 类型和值的定义
END
示例(定义一个 SNMP MIB 对象):
asn.1
SNMP-MIB DEFINITIONS ::= BEGIN
sysUpTime OBJECT-TYPE
SYNTAX INTEGER (0..4294967295)
MAX-ACCESS read-only
STATUS current
DESCRIPTION "系统运行时间(单位:0.01秒)"
::= { system 3 }
END
- 对象标识符(OID) • OID 是一个树状结构的全局唯一标识符,用于标识被管理对象(如 SNMP 中的 MIB 对象)。 • 语法 :用点分十进制表示,例如
1.3.6.1.2.1.1.1
对应iso.org.dod.internet.mgmt.mib-2.system.sysDescr
。
- 宏(MACRO) • ASN.1 支持通过宏(
MACRO
)扩展自定义语法(如 SNMP 中的OBJECT-TYPE
宏):
asn.1
OBJECT-TYPE MACRO ::= BEGIN
TYPE NOTATION ::= "SYNTAX" type "ACCESS" access "STATUS" status
VALUE NOTATION ::= value(VALUE OBJECT IDENTIFIER)
END
二、ASN.1 编码规则 ASN.1 定义了多种编码规则,将数据结构转换为二进制或文本格式。常见规则包括:
-
BER(Basic Encoding Rules) • 用途 :SNMPv1/v2c 使用 BER 编码。 • 编码格式 :每个数据元素由 标识符(Tag) 、长度(Length) 和 值(Value) 组成(TLV 结构)。 • 示例 :整数
42
的 BER 编码:plaintext02 01 2A ├── 02: 标识符(INTEGER 类型) ├── 01: 长度(1字节) └── 2A: 值(十六进制的 42)
IMPORTS
关键字用于从其他模块中引入(导入)已定义的数据类型或值
最开始的定义引用了SMI的定义,从1155中复制过来
sql
-- names of objects in the MIB
ObjectName ::=
OBJECT IDENTIFIER
-- syntax of objects in the MIB
ObjectSyntax ::=
CHOICE {
simple
SimpleSyntax,
-- note that simple SEQUENCEs are not directly
-- mentioned here to keep things simple (i.e.,
-- prevent mis-use). However, application-wide
-- types which are IMPLICITly encoded simple
-- SEQUENCEs may appear in the following CHOICE
application-wide
ApplicationSyntax
}
SimpleSyntax ::=
CHOICE {
number
INTEGER,
string
OCTET STRING,
object
OBJECT IDENTIFIER,
empty
NULL
}
ApplicationSyntax ::=
CHOICE {
address
NetworkAddress,
counter
Counter,
gauge
Gauge,
ticks
TimeTicks,
arbitrary
Opaque
-- other application-wide types, as they are
-- defined, will be added here
}
-- application-wide types
NetworkAddress ::=
CHOICE {
internet
IpAddress
}
IpAddress ::=
[APPLICATION 0] -- in network-byte order
IMPLICIT OCTET STRING (SIZE (4))
Counter ::=
[APPLICATION 1]
IMPLICIT INTEGER (0..4294967295)
Gauge ::=
[APPLICATION 2]
IMPLICIT INTEGER (0..4294967295)
TimeTicks ::=
[APPLICATION 3]
IMPLICIT INTEGER (0..4294967295)
Opaque ::=
[APPLICATION 4] -- arbitrary ASN.1 value,
IMPLICIT OCTET STRING -- "double-wrapped"
这上面出现了一个 APPLICATION的标签
ASN.1 定义了 4 类标签,用于区分不同类型的上下文:
标签类 | 说明 |
---|---|
UNIVERSAL | 通用标签,预定义类型(如 INTEGER 、OCTET STRING )必须使用此类标签。 |
APPLICATION | 应用范围标签,用于某个特定应用(如 SNMP、LDAP)内部的自定义类型。 |
CONTEXT-SPECIFIC | 上下文相关标签,仅在某个结构(如 SEQUENCE )内部有效。 |
PRIVATE | 私有标签,供组织内部使用。 |
asn.1
TimeTicks ::= [APPLICATION 3] IMPLICIT INTEGER (0..4294967295)
• **APPLICATION 3**:表示这是一个 **应用范围的自定义类型**,标签号为 `3`。
• **IMPLICIT**:表示编码时直接使用 `APPLICATION 3` 标签,而不是继承基类型(`INTEGER`)的 UNIVERSAL 标签。
3. 编码示例**
假设 `TimeTicks` 值为 `12345`(即 123.45秒):
• **默认编码(无标签)**:
作为普通 `INTEGER` 会被编码为 `02 02 30 39`(`0x02` 是 UNIVERSAL 标签)。
• **使用 `APPLICATION 3` 标签后**:
编码为 `43 02 30 39`:
• `43` = `APPLICATION 3` 的标签号(`0x40` 表示 APPLICATION 类,`0x03` 是标签值,故 `0x40 + 0x03 = 0x43`)。
• `02` = 长度(2字节)。
• `30 39` = 值 `12345` 的十六进制(`0x3039`)。
4.1.1 常用的ASN.1 constructs used frequently
scss
-- request/response information
RequestID ::=
INTEGER
ErrorStatus ::=
INTEGER {
noError(0),
tooBig(1),
noSuchName(2),
badValue(3),
readOnly(4)
genErr(5)
}
ErrorIndex ::=
INTEGER
-- variable bindings
VarBind ::=
SEQUENCE {
name
ObjectName,
value
ObjectSyntax
}
VarBindList ::=
SEQUENCE OF
VarBind
4.1.2 The GetRequest-PDU
由协议实体生成收到 GetRequest-PDU 后, 接收协议实体根据下面列表中的任何适用规则进行响应
接收协议实体向接收消息的发起者发送GetResponse-PDU
lua
GetRequest-PDU ::=
[0]
IMPLICIT SEQUENCE {
request-id
RequestID,
error-status -- always 0
ErrorStatus,
error-index -- always 0
ErrorIndex,
variable-bindings
VarBindList
}
需要检查 variable-bindings 处理noSuchName异常以及genErr异常, 检查是否超过本地限制tooBig
scss
noError(0),tooBig(1),noSuchName(2),genErr(5)
4.1.3 GetNextRequest-PDU
GetNextRequest-PDU 的格式与 GetRequest-PDU 的格式相同, 只是 PDU 类型的指示不同
GetNextRequest-PDU 仅由协议实体在其 SNMP 应用实体的请求下生成。 在收到 GetNextRequest-PDU 后, 接收协议实体将根据以下列表中的任何适用规则作出响应
ini
Destination NextHop Metric
10.0.0.99 89.1.1.42 5
9.1.2.3 99.0.0.3 3
10.0.0.51 89.1.1.42 5
The management station sends to the SNMP agent a GetNextRequest-PDU
containing the indicated OBJECT IDENTIFIER values as the requested
variable names:
GetNextRequest ( ipRouteDest, ipRouteNextHop, ipRouteMetric1 )
The SNMP agent responds with a GetResponse-PDU:
GetResponse (( ipRouteDest.9.1.2.3 = "9.1.2.3" ),
( ipRouteNextHop.9.1.2.3 = "99.0.0.3" ),
( ipRouteMetric1.9.1.2.3 = 3 ))
The management station continues with:
GetNextRequest ( ipRouteDest.9.1.2.3,
ipRouteNextHop.9.1.2.3,
ipRouteMetric1.9.1.2.3 )
The SNMP agent responds:
GetResponse (( ipRouteDest.10.0.0.51 = "10.0.0.51" ),
( ipRouteNextHop.10.0.0.51 = "89.1.1.42" ),
( ipRouteMetric1.10.0.0.51 = 5 ))
The management station continues with:
GetNextRequest ( ipRouteDest.10.0.0.51,
ipRouteNextHop.10.0.0.51,
ipRouteMetric1.10.0.0.51 )
The SNMP agent responds:
GetResponse (( ipRouteDest.10.0.0.99 = "10.0.0.99" ),
( ipRouteNextHop.10.0.0.99 = "89.1.1.42" ),
( ipRouteMetric1.10.0.0.99 = 5 ))
The management station continues with:
GetNextRequest ( ipRouteDest.10.0.0.99,
ipRouteNextHop.10.0.0.99,
ipRouteMetric1.10.0.0.99 )
As there are no further entries in the table, the SNMP agent returns
those objects that are next in the lexicographical ordering of the
known object names. This response signals the end of the routing
table to the management station.
4.1.4. The GetResponse-PDU
GetResponse-PDU 的格式与 GetRequest-PDU 的格式相同, 只是 PDU 类型指示不同
接收到 GetRequest-PDU、GetNextRequest-PDU 或 SetRequest-PDU 协议实体才会生成 GetResponse-PDU如本文档其他地方所述。
在收到 GetResponse-PDU 后, 接收协议实体会将其内容呈现给其 SNMP 应用程序实体
4.1.5 SetRequest -PDU
SetRequest-PDU 的格式与 GetRequest-PDU 相同, 只是 PDU 类型指示不同
SNMP 应用程序实体的请求后,协议实体才会生成 SetRequest-PDU 收到 SetRequest-PDU 后, 接收实体根据下面列表中的任何适用规则进行响应
4.1.6. The Trap-PDU
Trap-PDU 格式 :
sql
Trap-PDU ::=
[4]
IMPLICIT SEQUENCE {
enterprise -- type of object generating
-- trap, see sysObjectID in [5]
OBJECT IDENTIFIER,
agent-addr -- address of object generating
NetworkAddress, -- trap
generic-trap -- generic trap type
INTEGER {
coldStart(0),
warmStart(1),
linkDown(2),
linkUp(3),
authenticationFailure(4),
egpNeighborLoss(5),
enterpriseSpecific(6)
},
specific-trap -- specific code, present even
INTEGER, -- if generic-trap is not
-- enterpriseSpecific
time-stamp -- time elapsed between the last
TimeTicks, -- (re)initialization of the network
-- entity and the generation of the
trap
variable-bindings -- "interesting" information
VarBindList
}
SNMP 应用程序实体的请求下,协议实体才会生成 Trap-PDU
实体选择 SNMP 应用程序实体的目标地址的方式是特定于实现的。
在收到 Trap-PDU 后,接收协议实体将其内容呈现给其 SNMP 应用程序实体 Trap-PDU 的变量绑定组件的意义与实现有关。
5 定义
sql
RFC1157-SNMP DEFINITIONS ::= BEGIN
-- IMPORTS ObjectName, ObjectSyntax, NetworkAddress, IpAddress, TimeTicks FROM RFC1155-SMI;
-- names of objects in the MIB
ObjectName ::=
OBJECT IDENTIFIER
-- syntax of objects in the MIB
ObjectSyntax ::=
CHOICE {
simple
SimpleSyntax,
-- note that simple SEQUENCEs are not directly
-- mentioned here to keep things simple (i.e.,
-- prevent mis-use). However, application-wide
-- types which are IMPLICITly encoded simple
-- SEQUENCEs may appear in the following CHOICE
application-wide
ApplicationSyntax
}
SimpleSyntax ::=
CHOICE {
number
INTEGER,
string
OCTET STRING,
object
OBJECT IDENTIFIER,
empty
NULL
}
ApplicationSyntax ::=
CHOICE {
address
NetworkAddress,
counter
Counter,
gauge
Gauge,
ticks
TimeTicks,
arbitrary
Opaque
-- other application-wide types, as they are
-- defined, will be added here
}
-- application-wide types
NetworkAddress ::=
CHOICE {
internet
IpAddress
}
IpAddress ::=
[APPLICATION 0] -- in network-byte order
IMPLICIT OCTET STRING (SIZE (4))
Counter ::=
[APPLICATION 1]
IMPLICIT INTEGER (0..4294967295)
Gauge ::=
[APPLICATION 2]
IMPLICIT INTEGER (0..4294967295)
TimeTicks ::=
[APPLICATION 3]
IMPLICIT INTEGER (0..4294967295)
Opaque ::=
[APPLICATION 4] -- arbitrary ASN.1 value,
IMPLICIT OCTET STRING -- "double-wrapped"
-- top-level message
Message ::=
SEQUENCE {
version -- version-1 for this RFC
INTEGER {
version-1(0)
},
community -- community name
OCTET STRING,
data -- e.g., PDUs if trivial
ANY -- authentication is being used
}
-- protocol data units
PDUs ::=
CHOICE {
get-request
GetRequest-PDU,
get-next-request
GetNextRequest-PDU,
get-response
GetResponse-PDU,
set-request
SetRequest-PDU,
trap
Trap-PDU
}
-- PDUs
GetRequest-PDU ::=
[0]
IMPLICIT PDU
GetNextRequest-PDU ::=
[1]
IMPLICIT PDU
GetResponse-PDU ::=
[2]
IMPLICIT PDU
SetRequest-PDU ::=
[3]
IMPLICIT PDU
PDU ::=
SEQUENCE {
request-id
INTEGER,
error-status -- sometimes ignored
INTEGER {
noError(0),
tooBig(1),
noSuchName(2),
badValue(3),
readOnly(4),
genErr(5)
},
error-index -- sometimes ignored
INTEGER,
variable-bindings -- values are sometimes ignored
VarBindList
}
Trap-PDU ::=
[4]
IMPLICIT SEQUENCE {
enterprise -- type of object generating
-- trap, see sysObjectID in [5]
OBJECT IDENTIFIER,
agent-addr -- address of object generating
NetworkAddress, -- trap
generic-trap -- generic trap type
INTEGER {
coldStart(0),
warmStart(1),
linkDown(2),
linkUp(3),
authenticationFailure(4),
egpNeighborLoss(5),
enterpriseSpecific(6)
},
specific-trap -- specific code, present even
INTEGER, -- if generic-trap is not
-- enterpriseSpecific
time-stamp -- time elapsed between the last
TimeTicks, -- (re)initialization of the
network
-- entity and the generation of the
trap
variable-bindings -- "interesting" information
VarBindList
}
-- variable bindings
VarBind ::=
SEQUENCE {
name
ObjectName,
value
ObjectSyntax
}
VarBindList ::=
SEQUENCE OF
VarBind
END