SNMP V1 Trap 프로토콜 디코더의 구현 학술

예제와 함께 알아보는 SNMP Trap 프로토콜의 구현.. 설명과 하단의 소스 참고하세요..
구현할 때는 SNMP 프로토콜 자체보다 ASN.1과 BER/DER 인코딩 방식을 더 잘 알아야 된다는..

version / community
v1인 경우 enterprise oid / sender address / trap type / specific code / timestamp
v2인 경우 request id / error status / error index
(v2에서 enterprise oid와 timestamp는 variable binding으로 빠짐..)

trapgen -d 127.0.0.1 -c nchovy -i 10.0.1.123 -g 6 -s 0

Length: 43
=======================
30 29 02 01 00 04 06 6E
63 68 6F 76 79 A4 1C 06
07 2B 06 01 04 01 96 26
40 04 0A 00 01 7B 02 01
06 02 01 00 43 03 13 65
3C 30 00
=======================

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
 }

RFC1155를 보면 SEQUENCE는 리스트나 테이블을 생성하는데 사용됨. ASN.1을 BER(Basic Encoding Rules) / DER (Distinguished Encoding Rules)로 인코딩 하는데, X.690 Information technology – ASN.1 encoding rules 명세를 봐야 함.

데이터는 보통 식별자 바이트 / 길이 바이트 / 내용 바이트들로 구성된다. 마지막에 내용 끝(0x00 0x00)이 붙는 경우도 있다.  식별자 바이트는 ASN.1 태그를 인코드하여 타입을 표시한다. 식별자 바이트 맨 앞 2비트(8/7번)는 클래스, 뒤쪽 5비트가 태그 번호를 표현한다.

SEQUENCE의 경우 0x30과 전체 길이로 인코딩함. 따라서 시작부에 30 29라고 나온 것은 SEQUENCE 타입으로 41바이트 (0x29)가 있다는 의미임. 그 다음에 version이 02 (int 타입) 01 (길이 1) 00 (0은 버전1 의미), community가 04 (octet string 타입) 06 (길이 6) 6E (n) 63 (c) 68 (h) 6F (o) 76 (v) 79 (y) 그리고 PDU 나오기 시작함.

추가> 길이가 0x80 (128) 이상인 경우 long form으로 길이를 표현해야 한다. long form의 경우 8번 비트를 1로 한 상태에서 나머지 비트로 길이를 표현하는 바이트의 수(unsigned로 취급되는)를 표현한다. 따라서 0x82라는 것은 2바이트를 더 길이로 쓰겠다는 것이고, 뒤에 나오는 2바이트는 길게 이어붙은 unsigned 비트열이라고 생각하면 된다. (8.1.3.5절 참조) 현실적으로 3만 바이트 이상의 UDP 패킷이 존재할 가능성은 없으므로, SNMP 전용으로 ASN 디코더를 야매로 짜서 길이를 최대 추가 2바이트까지만 처리하게 하는 경우도 있다.

PDU는 아래와 같이 정의되고..
PDUs ::=
 CHOICE {
     get-request
         GetRequest-PDU,

     get-next-request
         GetNextRequest-PDU,

     get-response
         GetResponse-PDU,

     set-request
         SetRequest-PDU,

     trap
         Trap-PDU
      }

Trap이므로 Trap PDU를 봐야 하는데.. CHOICE는 별도로 인코딩되는건 아니고 그냥 선택된 것의 식별자 태그부터 쭉 나온다.

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
 }

Trap1PDU의 타입은 0xA4로 인코딩되고 1C는 28 바이트 길이를 의미한다. 이후 06 07 2B 06 01 04 01 96 26 은 OID (Object Identifier) 타입으로 Enterprise OID를 의미한다. 0x06이 OID 타입이고, 0x07은 길이이고, 값은 다음과 같은 규칙으로 인코딩된다. 이게 좀 황당하게도 OID에서 첫 2개 숫자를 X * 40 + Y 식으로 합쳐서 악착같이 바이트를 아끼고 있고..  etri_oid.pdf 이 문서에 OID 인코딩 방식이 잘 설명되어 있다. 하나의 큰 숫자를 표현할 때 7비트씩 묶은 다음 이어지는건 맨 앞 (8번) 비트를 1로, 마지막 바이트의 8번 비트는 0으로 해서 비트열을 만드는 방식이다. 따라서 127 이하의 숫자만 1바이트로 표시된다. 따라서 위의 2B 06 01 04 01 96 26에서 96 26이 각각 10010110 과 00100110 이므로 101100100110이고 2854가 된다. 그러므로 .1.3.6.1.4.1.2854가 된다.

0x40은 네트워크 어드레스, 0x43은 TimeTicks (1/100초 단위), 마지막 0x30은 변수 바인딩 시퀀스 부분

Full 구현되지는 않았지만 대충 이 정도 설명에다가 감 잡고 쓸만한 정도로 구현된 C# 소스를 첨부하니 참고하세요.. (버그 많음)

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://www.xeraph.com/tb/5048163 [도움말]

덧글

댓글 입력 영역