SNMP 协议 分析 抓包 Protocol Specification 拆包实践

 

gosnmp/main.go at v1.35.0 · gosnmp/gosnmp · GitHub https://github.com/gosnmp/gosnmp/blob/v1.35.0/examples/example3/main.go

params := &g.GoSNMP{
Target: "192.168.91.20",
Port: 161,
Version: g.Version3,
SecurityModel: g.UserSecurityModel,
MsgFlags: g.AuthPriv,
Timeout: time.Duration(30) * time.Second,
SecurityParameters: &g.UsmSecurityParameters{UserName: "user",
AuthenticationProtocol: g.SHA,
AuthenticationPassphrase: "password",
PrivacyProtocol: g.DES,
PrivacyPassphrase: "password",
},
}

 

$ snmpwalk --help
USAGE: snmpwalk [OPTIONS] AGENT [OID]

Version: 5.7.2
Web: http://www.net-snmp.org/
Email: net-snmp-coders@lists.sourceforge.net

OPTIONS:
-h, --help display this help message
-H display configuration file directives understood
-v 1|2c|3 specifies SNMP version to use
-V, --version display package version number
SNMP Version 1 or 2c specific
-c COMMUNITY set the community string
SNMP Version 3 specific
-a PROTOCOL set authentication protocol (MD5|SHA)
-A PASSPHRASE set authentication protocol pass phrase
-e ENGINE-ID set security engine ID (e.g. 800000020109840301)
-E ENGINE-ID set context engine ID (e.g. 800000020109840301)
-l LEVEL set security level (noAuthNoPriv|authNoPriv|authPriv)
-n CONTEXT set context name (e.g. bridge1)
-u USER-NAME set security name (e.g. bert)
-x PROTOCOL set privacy protocol (DES|AES)
-X PASSPHRASE set privacy protocol pass phrase
-Z BOOTS,TIME set destination engine boots/time
General communication options
-r RETRIES set the number of retries
-t TIMEOUT set the request timeout (in seconds)
Debugging
-d dump input/output packets in hexadecimal
-D[TOKEN[,...]] turn on debugging output for the specified TOKENs
(ALL gives extremely verbose debugging output)
General options
-m MIB[:...] load given list of MIBs (ALL loads everything)
-M DIR[:...] look in given list of directories for MIBs
(default: $HOME/.snmp/mibs:/usr/local/share/snmp/mibs)
-P MIBOPTS Toggle various defaults controlling MIB parsing:
u: allow the use of underlines in MIB symbols
c: disallow the use of "--" to terminate comments
d: save the DESCRIPTIONs of the MIB objects
e: disable errors when MIB symbols conflict
w: enable warnings when MIB symbols conflict
W: enable detailed warnings when MIB symbols conflict
R: replace MIB symbols from latest module
-O OUTOPTS Toggle various defaults controlling output display:
0: print leading 0 for single-digit hex characters
a: print all strings in ascii format
b: do not break OID indexes down
e: print enums numerically
E: escape quotes in string indices
f: print full OIDs on output
n: print OIDs numerically
q: quick print for easier parsing
Q: quick print with equal-signs
s: print only last symbolic element of OID
S: print MIB module-id plus last element
t: print timeticks unparsed as numeric integers
T: print human-readable text along with hex strings
u: print OIDs using UCD-style prefix suppression
U: don't print units
v: print values only (not OID = value)
x: print all strings in hex format
X: extended index format
-I INOPTS Toggle various defaults controlling input parsing:
b: do best/regex matching to find a MIB node
h: don't apply DISPLAY-HINTs
r: do not check values for range/type legality
R: do random access to OID labels
u: top-level OIDs must have '.' prefix (UCD-style)
s SUFFIX: Append all textual OIDs with SUFFIX before parsing
S PREFIX: Prepend all textual OIDs with PREFIX before parsing
-L LOGOPTS Toggle various defaults controlling logging:
e: log to standard error
o: log to standard output
n: don't log at all
f file: log to the specified file
s facility: log to syslog (via the specified facility)

(variants)
[EON] pri: log to standard error, output or /dev/null for level 'pri' and above
[EON] p1-p2: log to standard error, output or /dev/null for levels 'p1' to 'p2'
[FS] pri token: log to file/syslog for level 'pri' and above
[FS] p1-p2 token: log to file/syslog for levels 'p1' to 'p2'
-C APPOPTS Set various application specific behaviours:
p: print the number of variables found
i: include given OID in the search range
I: don't include the given OID, even if no results are returned
c: do not check returned OIDs are increasing
t: Display wall-clock time to complete the walk
T: Display wall-clock time to complete each request
E {OID}: End the walk at the specified OID

 

 

 

 

rfc1157 https://datatracker.ietf.org/doc/html/rfc1157#section-3.2.2

 

4.  Protocol Specification

   The network management protocol is an application protocol by which
   the variables of an agent's MIB may be inspected or altered.

   Communication among protocol entities is accomplished by the exchange
   of messages, each of which is entirely and independently represented
   within a single UDP datagram using the basic encoding rules of ASN.1
   (as discussed in Section 3.2.2).  A message consists of a version
   identifier, an SNMP community name, and a protocol data unit (PDU).
   A protocol entity receives messages at UDP port 161 on the host with
   which it is associated for all messages except for those which report
   traps (i.e., all messages except those which contain the Trap-PDU).
   Messages which report traps should be received on UDP port 162 for
   further processing.  An implementation of this protocol need not
   accept messages whose length exceeds 484 octets.  However, it is
   recommended that implementations support larger datagrams whenever
   feasible.

   It is mandatory that all implementations of the SNMP support the five
   PDUs:  GetRequest-PDU, GetNextRequest-PDU, GetResponse-PDU,
   SetRequest-PDU, and Trap-PDU.

    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
                     }






Case, Fedor, Schoffstall, & Davin                              [Page 16]


RFC 1157                          SNMP                          May 1990


     -- 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

 

packet:PACKET: 234 bytes, wire length 234 cap length 234 @ 2021-11-01 19:37:02.933491 +0800 CST

- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..220..] SrcMAC=00:0 c:17:43:9b:44 DstMAC=1c:98:ec:2a:75:18 EthernetType=IPv4 Length=0}

- Layer 2 (20 bytes) = IPv4 {Contents=[..20..] Payload=[..200..] Version=4 IHL=5 TOS=0 Length=220 Id=45535 Flags=DF FragOffset=0 TTL=64 Protocol=UDP Checksu

m=14166 SrcIP=10.20.30.171 DstIP=10.20.30.9 Options=[] Padding=[]}

- Layer 3 (08 bytes) = UDP {Contents=[..8..] Payload=[..192..] SrcPort=31453 DstPort=162(snmptrap) Length=200 Checksum=52923}

- Layer 4 (192 bytes) = Payload 192 byte(s)

UDP:&{{[122 221 0 162 0 200 206 187] [

(48 129  189)  (2 1 1 )(4 6  112 117 98 108 105 99) (167 129 175)(2 4 31 169 179 27)(2 1 0 2 1 0) (48 129  160 )  

(48 16  6 8  43 6 1 2 1 1 3 0            67 4   2 175 245 221 )  

(48 25  6 10  43 6 1 6 3 1 1 4 1 0        6 11   43 6 1 4 1 129 224 41 1 8 11)  

(48 16  6 11  43 6 1 4 1 129 224 41 3 1 1  2 1  0)  

(48 64  6 11  43 6 1 4 1 129 224 41 3 1 2  4 49  77 111 117 110 116 32 108 111 99 97 116 105 111 110 32 47 100 97

116 97 32 104 97 115 32 101 120 99 101 101 100 101 100 32 117 115 97 103 101 32

116 104 114 101 115 104 111 108 100 )

(48 29  6 11  43 6 1 4 1 129 224 41 3 1 3  6 14  43 6 1 4 1 129 224 41 1 5 1 1 5 1)

 

]} 31453 162(snmptrap) 200 52923 [122 221] [0 162] {<nil>}}

packet:PACKET: 269 bytes, wire length 269 cap length 269 @ 2021-11-01 19:37:24.239214 +0800 CST

- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..255..] SrcMAC=00:0c:17:49:23:c4 DstMAC=1c:98:ec:2a:75:18 EthernetType=IPv4 Length=0}

- Layer 2 (20 bytes) = IPv4 {Contents=[..20..] Payload=[..235..] Version=4 IHL=5 TOS=0 Length=255 Id=14536 Flags=DF FragOffset=0 TTL=64 Protocol=UDP Checksu

m=45270 SrcIP=10.20.30.31 DstIP=10.20.30.9 Options=[] Padding=[]}

- Layer 3 (08 bytes) = UDP {Contents=[..8..] Payload=[..227..] SrcPort=3255 9 DstPort=162(snmptrap) Length=235 Checksum=59772}

- Layer 4 (227 bytes) = Payload 227 byte(s)

UDP:&{{[127 47 0 162 0 235 233 124] [

(48 129 224) (2 1 1) (4 6 112 117 98 108 105 99) (167 129 210)( 2 4 13 129 120 77)  (2 1 0 2 1 0)  (48 129 195) 

(48 16  6 8   43 6 1 2 1 1 3 0            67 4  2 220 87 227)

(48 25  6 10  43 6 1 6 3 1 1 4 1 0         6 11  43 6 1 4 1 129 224 41 1 8 23)  

(48 16  6 11  43 6 1 4 1 129 224 41 3 1 1   2 1  0)

(48 99  6 11  43 6 1 4 1 129 224 4 1 3 1 2   4 84  72 68 68 32 83 77 65 82 84 32 97 116 116 114 105 98 117 116 101 32

39 85 68 77 65 95 67 82 67 95 69 114 114 111 114 95 67 111 117 110 116 39 32 102

111 114 32 100 105 115 107 32 39 80 83 68 45 115 108 111 116 45 51 39 32 99 104

97 110 103 101 100 32 102 114 111 109 32 48 32 116 111 32 49)

(48 29  6 11  43 6 1 4 1 129 224 41 3 1 3  6 14  43 6 1 4 1 129 224 41 1 3 1 1 14 2)

 

]} 32559 162(snmptra p) 235 59772 [127 47] [0 162] {<nil>}}

 

主体结构:

(类型-1个字节,字节数-1个字节,内容)

 

packet:PACKET: 234 bytes, wire length 234 cap length 234 @ 2021-11-01 19:37:02.9

33491 +0800 CST

- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..220..] SrcMAC=00:0

c:17:43:9b:44 DstMAC=1c:98:ec:2a:75:18 EthernetType=IPv4 Length=0}

- Layer 2 (20 bytes) = IPv4     {Contents=[..20..] Payload=[..200..] Version=4 I

HL=5 TOS=0 Length=220 Id=45535 Flags=DF FragOffset=0 TTL=64 Protocol=UDP Checksu

m=14166 SrcIP=10.20.30.171 DstIP=10.20.30.9 Options=[] Padding=[]}

- Layer 3 (08 bytes) = UDP      {Contents=[..8..] Payload=[..192..] SrcPort=3145

3 DstPort=162(snmptrap) Length=200 Checksum=52923}

- Layer 4 (192 bytes) = Payload 192 byte(s)

 

UDP:&{{[122 221 0 162 0 200 206 187] [(UDP payload)]} 31453 162(snmptrap) 200 52923 [122 221] [0 1

62] {<nil>}}

 

UDP payload:[48 129 189 2 1 1 4 6 112 117 98 108 105 99 167 129 175 2 4 31 169 1

79 27 2 1 0 2 1 0 48 129 160 48 16 6 8 43 6 1 2 1 1 3 0 67 4 2 175 245 221 48 25

 6 10 43 6 1 6 3 1 1 4 1 0 6 11 43 6 1 4 1 129 224 41 1 8 11 48 16 6 11 43 6 1 4

 1 129 224 41 3 1 1 2 1 0 48 64 6 11 43 6 1 4 1 129 224 41 3 1 2 4 49 77 111 117

 110 116 32 108 111 99 97 116 105 111 110 32 47 100 97 116 97 32 104 97 115 32 1

01 120 99 101 101 100 101 100 32 117 115 97 103 101 32 116 104 114 101 115 104 1

11 108 100 48 29 6 11 43 6 1 4 1 129 224 41 3 1 3 6 14 43 6 1 4 1 129 224 41 1 5

 1 1 5 1]

 

 

packet:PACKET: 269 bytes, wire length 269 cap length 269 @ 2021-11-01 19:37:24.2

39214 +0800 CST

- Layer 1 (14 bytes) = Ethernet {Contents=[..14..] Payload=[..255..] SrcMAC=00:0

c:17:49:23:c4 DstMAC=1c:98:ec:2a:75:18 EthernetType=IPv4 Length=0}

- Layer 2 (20 bytes) = IPv4     {Contents=[..20..] Payload=[..235..] Version=4 I

HL=5 TOS=0 Length=255 Id=14536 Flags=DF FragOffset=0 TTL=64 Protocol=UDP Checksu

m=45270 SrcIP=10.20.30.31 DstIP=10.20.30.9 Options=[] Padding=[]}

- Layer 3 (08 bytes) = UDP      {Contents=[..8..] Payload=[..227..] SrcPort=3255

9 DstPort=162(snmptrap) Length=235 Checksum=59772}

- Layer 4 (227 bytes) = Payload 227 byte(s)

 

 

UDP:&{{[127 47 0 162 0 235 233 124] [(UDP payload)]} 32559 162(snmptra

p) 235 59772 [127 47] [0 162] {<nil>}}

 

UDP payload:[48 129 224 2 1 1 4 6 112 117 98 108 105 99 167 129 210 2 4 13 129 1

20 77 2 1 0 2 1 0 48 129 195 48 16 6 8 43 6 1 2 1 1 3 0 67 4 2 220 87 227 48 25

6 10 43 6 1 6 3 1 1 4 1 0 6 11 43 6 1 4 1 129 224 41 1 8 23 48 16 6 11 43 6 1 4

1 129 224 41 3 1 1 2 1 0 48 99 6 11 43 6 1 4 1 129 224 41 3 1 2 4 84 72 68 68 32

 83 77 65 82 84 32 97 116 116 114 105 98 117 116 101 32 39 85 68 77 65 95 67 82

67 95 69 114 114 111 114 95 67 111 117 110 116 39 32 102 111 114 32 100 105 115

107 32 39 80 83 68 45 115 108 111 116 45 51 39 32 99 104 97 110 103 101 100 32 1

02 114 111 109 32 48 32 116 111 32 49 48 29 6 11 43 6 1 4 1 129 224 41 3 1 3 6 1

4 43 6 1 4 1 129 224 41 1 3 1 1 14 2]  

 

 

EndOfContents     Asn1BER = 0x00

UnknownType       Asn1BER = 0x00

Boolean           Asn1BER = 0x01

Integer           Asn1BER = 0x02

BitString         Asn1BER = 0x03

OctetString       Asn1BER = 0x04

Null              Asn1BER = 0x05

ObjectIdentifier  Asn1BER = 0x06

ObjectDescription Asn1BER = 0x07

IPAddress         Asn1BER = 0x40

Counter32         Asn1BER = 0x41

Gauge32           Asn1BER = 0x42

TimeTicks         Asn1BER = 0x43

Opaque            Asn1BER = 0x44

NsapAddress       Asn1BER = 0x45

Counter64         Asn1BER = 0x46

Uinteger32        Asn1BER = 0x47

OpaqueFloat       Asn1BER = 0x78

OpaqueDouble      Asn1BER = 0x79

NoSuchObject      Asn1BER = 0x80

NoSuchInstance    Asn1BER = 0x81

EndOfMibView      Asn1BER = 0x82

 

 

 

[转] Snmpv1 v2报文格式详细介绍 - CurrentJ - ITeye博客 https://www.iteye.com/blog/currentj-2280760

SNMP的报文格式

SNMP代理和管理站通过SNMP协议中的标准消息进行通信,每个消息都是一个单独的数据报。SNMP使用UDP(用户数据报协议)作为第四层协议(传输协议),进行无连接操作。SNMP消息报文包含两个部分:SNMP报头和协议数据单元PDU。

 

 

 

在实际网络传输环境下,SNMP报文的长度取决于其所采用的编码方式。SNMP统一采用BER(Basic Encoding Rule)的编码规则,同时在正式SNMP规范中使用的是ASN.1语法,AbastractSyntax Notation v1,即抽象语法描述语言。这两个概念在后面实践环节再做进一步介绍,这里只要稍微了解一下即可,不妨碍我们对协议本身的分析。这里我们简单解释一下BER编码规则:

    BER作为ANS.1的基本编码规则,描述具体的ANS.1对象如何编码为比特流在网络上进行传输。BER编码规则由三部分组成:

 

 

 

 

SNMP中定义了几种基本的数据类型,其中v1和v2版有些改动,具体参见相应的RFC文档。这里我们只介绍几种最常见的类型:

l  INTEGER:一个整数

l  OCTER STRING: 0或多个8bit字节,每个字节在0~255之间取值

l  DisplayString:0或多个8bit字节,每个字节必须是ASCII码。在MIB-II中,所有该类型变量不能超过255个字符(0个字符可以)

l  NULL:代表相关的变量没有值

l  IpAddress:4字节长的OCTER STRING,以网络字节序表示IP地址

l  PhyAddress:6字节长的OCTER STRING,代表物理地址

l  Counter:非负整数,可以从0递增到232-1()。达到最大值后归0

l  TimeTicks:时间计数器,以0.01秒为单位递增,不同的变量可以有不同的递增幅度。所以在定义这种类型的变量时需要制定递增幅度

l  SEQUENCE:与C语言中的结构体类似

l  SEQUENCE OF:一个向量,参见后面ANS.1语法详细介绍章节

SNMP报文在传输层是封装在UDP报文中的,而UDP又是基于IP网络的,因此,我们可以得到完整的报文描述结构,如下图所示:

 

 

 

 

 PDU类型其实包含两个字节,第一个字节表示真实的PDU的类型;第二个字节表示后面报文所占的字节总数。针对SNMPv1,这个字段取值如下:

表1 PDU类型

PDU类型

   名 称

0

get-request

1

get-next-request

2

get-response

3

set-request

4

trap

 也就是说,trap的类型是4。但是在数据报文中,该字段一般表示为ax,其中x取[0,4],即a0~a3表示相应的get、set等操作,a4表示trap报文。这里除了类型字段意外,其他字段均采用BER编码方式:

 

 

实战演练之报文格式分析

    Trap报文格式和上述图5所展示的结构有些差别,这里我们只分析SNMPv1和SNMPv2的Trap报文格式。trap报文前面的部分都一样,区别在PDU协议数据单元部分。

SNMPv1报文

SNMPv1的Trap报文格式如下所示:

 

 

 

注意:除了PDU类型和PDU长度字段外,后面的每个字段都是BER编码方式。

    trap类型”可以取以下值,其中0~6是已定义的特定trap,7及其以后的类型由供应商自定义。

表2 trap类型、名称及描述信息

trap类型

名称

描述信息

0

coldStart

代理进程对自己初始化

1

warmStart

代理进程对自己重新初始化

2

linkDown

一个接口已从工作状态变为故障状态(报文中的第一个变量标识此接口)

3

linkUp

一个接口已从故障状态变为工作状态(报文中的第一个变量标识此接口)

4

authenticationFailure

从SNMP管理进程收到无效共同体的报文

5

egpNeighborLoss

一个EGP邻站已变为故障状态(报文中的第一个变量包含邻站IP地址)

6

enterpriseSpecific

在这个特定的代码段中查找trap信息

 

通过wireshark抓包工具,捕获一条如下的SNMP报文,接下来对其进行仔细分析。

SNMPv1原始报文内容:

00 23 5a 9e 58b9 00 4c 41 49 50 55 08 00 45 00 00 48 00 00 40 00 40 11 a5 4e c0 a80a 01 c0 a8  0a05 0c 00 00 a200 34 ff e0 30 2a 02 01 00 04 06 70 75 62 6c 69 63a4 1d 06 0a 2b 06 01 04 01 bf 08 03 02 0a 40 04 c0 a8 0a01 02 01 00 02 01 00 43 01 0e 30 00

 

目的MAC:00 23 5a 9e 58 b9

源MAC:00 4c 41 49 50 55

协议类型:08 00 ,为IP数据报

IP头:45 00 00 48 00 00 40 00 40 11 a5 4e c0 a80a 01 c0 a80a 05 0c

UDP头:0c 00 00 a2 00 34 ff e0

其余部分都为SNMP报文,接下来我们对照前面的报文结构体来逐个分析一下。

n  30 表示SNMP消息是ASN.1的SEQUENCE类型;

n  2a 表示该SNMP报文的总长度是42(0x2a)个字节,该字段所表示的报文长度起始于它后面的第一个字节直到报文结束;

n  02 01 00 表示版本号,可见其确实为BER编码方式。02表示该字段是INTEGER类型;01表示该字段占1个字节;00表示版本号,该值为“版本号-1”;

n  04 06 70 75 62 6c 69 63 表示团体名,04表示该字段为OCTETSTRING类型;06表示该字段占6个字节;70 75 62 6c 69 63表示团体名的ANSII码的十六进制形式,这里是“public”;

n  a4 1d 其中a4中的“4”表示这是一个trap报文,a4又叫报文的标签标记;1d表示后面还有29(0x1d)个字节的数据;

n  06 0a 2b 06 01 04 01 bf 08 03 02 0a 企业OID标识。06表示该字段是个对象标识符,OBJECTIDENTIFIER;0a表示该字段占10(0x0a)个字节;关于SNMP的OID的编码方式有些奇特:例如1.3.6.1.2…. 取前两个数字分别记为x和y。编码时40*x+y,这里x=1,y=3,因此结果为40*1+3=43,即表示十六进制的2b。因此,这里的企业OID编码即为1.3.6.1.4.1.8072.3.2.10;

n  40 04 c0 a80a 01 同样40表示该字段为OCTET STRING 类型;04表示IP地址占4个字节;IP地址为192.168.10.1;

n  02 01 00 其中00表示trap类型为coldStart;

n  02 01 00 其中00表示我们指定的trap即specific-trap也为coldStart类型;

n  43 01 0e 43表示为TimeTicks类型;01表示该字段占1个字节;0e即十进制的14表示时间标签为0.14秒,这里时间计数器以0.01秒递增;

n  30 00 30表示“键-值”值对的编码类型为SEQUENCE;00表示该字段占0个字节,即没有该字段。

 

SNMPv2报文 SNMPv2的Trap报文格式如图8所示:

 

 

同样的,这里除了trap类型和报文长度是标准网络字节序之外,其余协议字段也均为BER编码方式。可以看到v2版的trap报文正在向统一的报文格式发展,已经非常类似普通的SNMP请求、响应报文了。

SNMPv2原始报文内容:

00 23 5a 9e 58b9 00 4c 41 49 50 55 08 00 45 00 00 7b 00 00 40 00 40 11 a5 1b c0 a8 0a01 c0 a8 0a05 0c 01 00 a2 0067 04 bb 305d 02 01 01 04 06 70 75 62 6c 69 63a7 50 0204 1773 2c fb 02 01 00 02 01 00 30 42 30 0d 06 08 2b 06 01 02 01 01 03 00 43 01 0e 30 17 060a 2b 06 01 06 03 01 01 04 01 00 06 09 2b 06 01 0603 01 01 05 01 3018 060a2b 06 01 06 03 01 01 04 0300 06 0a2b 06 01 04 01 bf 08 03 02 0a

 

目的MAC:00 23 5a 9e 58 b9

源MAC:00 4c 41 49 50 55

协议类型:08 00,IP报文

IP头:45 00 00 7b 00 00 40 00 40 11 a5 1b c0 a80a 01 c0 a80a 05

UDP头:0c 01 00 a2 00 67 04 bb

余下部分全为SNMP报文内容,这里我们做一下简单的约定:

xx 标注类型;xx 标注长度;xx 标注真正的数据。

这样一来上面这串原始数据就好分析多了J

n  30 5d 整个SNMP报文的编码方式为30,即SEQUENCE类型,报文长度93(0x5d)字节;

n  02 01 01 版本号01即v2版本;

n  04 06 70 75 62 6c 69 63 团体名70 75 62 6c69 63  即英文的“public”;

n  a7 50 a7表示trap类型为7,即厂商自定义trap;50表示PDU区段占80(0x50)字节;

n  02 04 17 73 2c fb 请求ID为17 73 2c fb 十进制的393424123;

n  02 01 00 错误状态0;

n  02 01 00 错误索引0;

n  30 42 “变量名-值”对编码类型30 即SEQUENCE类型;“变量名-值”所占总字节0x42,即66字节;

n  30 0d 06 08 2b 06 01 02 01 01 03 00 43 01 0e 第一个“名-值”对区段编码方式30 即SEQUENCE类型;第一个“名-值”对总长度0x0d,13字节;第一个变量名的编码类型0x06,时间标签;第一个变量名占0x08个字节;第一个变量名2b 06 01 02 01 01 03 00,为1.3.6.1.2.1.1.3.0;第一个变量值为0x0e,即14;

n  30 17 06 0a2b 06 01 06 03 01 01 04 0100 06 09 2b 06 01 06 03 01 01 05 01 第二个“名-值”对;变量名1.3.6.1.6.3.1.1.4.1.0;变量值1.3.6.1.6.3.1.1.5.1;

n  30 18 06 0a2b 06 01 06 03 01 01 04 0300 06 0a2b 06 01 04 01 bf 08 03 02 0a 第三个“名-值”对;变量名1.3.6.1.6.3.1.1.4.3.0;变量值1.3.6.1.4.1.8072.3.2.10;

 

 

 

通过

"github.com/google/gopacket"获取UDP包,
将包交由
"github.com/gosnmp/gosnmp"
处理拆包。

 

snmpdata := v.Params.UnmarshalTrap(UDP.Payload, false)

github.com/gosnmp/gosnmp@v1.33.0/trap.go:363

// UnmarshalTrap unpacks the SNMP Trap.
//
// NOTE: the trap code is currently unreliable when working with snmpv3 - pull requests welcome
func (x *GoSNMP) UnmarshalTrap(trap []byte, useResponseSecurityParameters bool) (result *SnmpPacket) {
result = new(SnmpPacket)

if x.SecurityParameters != nil {
err := x.SecurityParameters.initSecurityKeys()
if err != nil {
return nil
}
result.SecurityParameters = x.SecurityParameters.Copy()
}

cursor, err := x.unmarshalHeader(trap, result)
if err != nil {
x.Logger.Printf("UnmarshalTrap: %s\n", err)
return nil
}

if result.Version == Version3 {
if result.SecurityModel == UserSecurityModel {
err = x.testAuthentication(trap, result, useResponseSecurityParameters)
if err != nil {
x.Logger.Printf("UnmarshalTrap v3 auth: %s\n", err)
return nil
}
}

trap, cursor, err = x.decryptPacket(trap, cursor, result)
if err != nil {
x.Logger.Printf("UnmarshalTrap v3 decrypt: %s\n", err)
return nil
}
}
err = x.unmarshalPayload(trap, cursor, result)
if err != nil {
x.Logger.Printf("UnmarshalTrap: %s\n", err)
return nil
}
return result
}

github.com/gosnmp/gosnmp@v1.33.0/marshal.go:923

// -- Unmarshalling Logic ------------------------------------------------------

func (x *GoSNMP) unmarshalHeader(packet []byte, response *SnmpPacket) (int, error) {
if len(packet) < 2 {
return 0, fmt.Errorf("cannot unmarshal empty packet")
}
if response == nil {
return 0, fmt.Errorf("cannot unmarshal response into nil packet reference")
}

response.Variables = make([]SnmpPDU, 0, 5)

// Start parsing the packet
cursor := 0

// First bytes should be 0x30
if PDUType(packet[0]) != Sequence {
return 0, fmt.Errorf("invalid packet header")
}

length, cursor, err := parseLength(packet)
if err != nil {
return 0, err
}
if len(packet) != length {
return 0, fmt.Errorf("error verifying packet sanity: Got %d Expected: %d", len(packet), length)
}
x.Logger.Printf("Packet sanity verified, we got all the bytes (%d)", length)

// Parse SNMP Version
rawVersion, count, err := parseRawField(x.Logger, packet[cursor:], "version")
if err != nil {
return 0, fmt.Errorf("error parsing SNMP packet version: %w", err)
}

cursor += count
if cursor > len(packet) {
return 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
}

if version, ok := rawVersion.(int); ok {
response.Version = SnmpVersion(version)
x.Logger.Printf("Parsed version %d", version)
}

if response.Version == Version3 {
oldcursor := cursor
cursor, err = x.unmarshalV3Header(packet, cursor, response)
if err != nil {
return 0, err
}
x.Logger.Printf("UnmarshalV3Header done. [with SecurityParameters]. Header Size %d. Last 4 Bytes=[%v]", cursor-oldcursor, packet[cursor-4:cursor])
} else {
// Parse community
rawCommunity, count, err := parseRawField(x.Logger, packet[cursor:], "community")
if err != nil {
return 0, fmt.Errorf("error parsing community string: %w", err)
}
cursor += count
if cursor > len(packet) {
return 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor)
}

if community, ok := rawCommunity.(string); ok {
response.Community = community
x.Logger.Printf("Parsed community %s", community)
}
}
return cursor, nil
}
func (x *GoSNMP) unmarshalPayload(packet []byte, cursor int, response *SnmpPacket) error {
if len(packet) == 0 {
return errors.New("cannot unmarshal nil or empty payload packet")
}
if cursor > len(packet) {
return fmt.Errorf("cannot unmarshal payload, packet length %d cursor %d", len(packet), cursor)
}
if response == nil {
return errors.New("cannot unmarshal payload response into nil packet reference")
}

// Parse SNMP packet type
requestType := PDUType(packet[cursor])
x.Logger.Printf("UnmarshalPayload Meet PDUType %#x. Offset %v", requestType, cursor)
switch requestType {
// known, supported types
case GetResponse, GetNextRequest, GetBulkRequest, Report, SNMPv2Trap, GetRequest, SetRequest, InformRequest:
response.PDUType = requestType
if err := x.unmarshalResponse(packet[cursor:], response); err != nil {
return fmt.Errorf("error in unmarshalResponse: %w", err)
}
// If it's an InformRequest, mark the trap.
response.IsInform = (requestType == InformRequest)
case Trap:
response.PDUType = requestType
if err := x.unmarshalTrapV1(packet[cursor:], response); err != nil {
return fmt.Errorf("error in unmarshalTrapV1: %w", err)
}
default:
x.Logger.Printf("UnmarshalPayload Meet Unknown PDUType %#x. Offset %v", requestType, cursor)
return fmt.Errorf("unknown PDUType %#x", requestType)
}
return nil
}

 

posted @ 2021-10-22 17:39  papering  阅读(1127)  评论(0编辑  收藏  举报