LoginSignup
3
6

More than 5 years have passed since last update.

SNMPのパケットキャプチャ&リプレイ

Last updated at Posted at 2016-04-26

SNMP(UDP)のパケットキャプチャ&リプレイのコードを書いてみました。
普通の人はSNMP-Simulatorを使うのが良いかと思いますが。。

使い方

  1. 192.168.1.119でSNMP-Agentを起動
  2. snmp_stub.pyをcaptureモードで実行
  3. snmpwalk -c public -v 1 192.168.1.12を実行
  4. captureファイルが作成されたことを確認する
  5. 192.168.1.119でSNMP-Agentを停止
  6. snmp_stub.pyをreplayモードで実行
  7. snmpwalk -c public -v 1 192.168.1.12を実行
  8. SNMP-Agentが起動している時と同等の結果が返ってくる

環境

OS X El Capitan
Python 2.7.11
snmpwalk 5.6.2.1
自PC:192.168.1.12
SNMP取得先:192168.1.119

実行結果

snmp_stub.pyの実行
$ sudo python snmp_stub.py

Requested: 0%26%02%01%00%04%06public%A1%19%02%04%3E%0B%CC%E5%02%01%00%02%01%000%0B0%09%06%05%2B%06%01%02%01%05%00 192.168.1.12 65344
Responsed: 0%81%AA%02%01%00%04%06public%A2%81%9C%02%04%3E%0B%CC%E5%02%01%00%02%01%000%81%8D0%81%8A%06%08%2B%06%01%02%01%01%01%00%04%7EDarwin%20macbook.local%2010.8.0%20Darwin%20Kernel%20Version%2010.8.0%3A%20Tue%20Jun%20%207%2016%3A33%3A36%20PDT%202011%3B%20root%3Axnu-1504.15.3%7E1/RELEASE_I386%20i386 192.168.1.12 65344
---------------------------
Requested: 0%29%02%01%00%04%06public%A1%1C%02%04%3E%0B%CC%E6%02%01%00%02%01%000%0E0%0C%06%08%2B%06%01%02%01%01%01%00%05%00 192.168.1.12 65344
Responsed: 03%02%01%00%04%06public%A2%26%02%04%3E%0B%CC%E6%02%01%00%02%01%000%180%16%06%08%2B%06%01%02%01%01%02%00%06%0A%2B%06%01%04%01%BF%08%03%02%10 192.168.1.12 65344
---------------------------
<省略>
snmpwalkの実行
$ snmpwalk -c public -v 1 192.168.1.12
SNMPv2-MIB::sysDescr.0 = STRING: Darwin macbook.local 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.16
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (27736720) 3 days, 5:02:47.20
SNMPv2-MIB::sysContact.0 = STRING: SysAdmin
<省略>

ソースコード

snmp_stub.py
import socket
import base64
import urllib

UDP_IP_LISTEN = "192.168.1.12"
UDP_PORT_LISTEN = 161
UDP_IP_TO = "192.168.1.119"
UDP_PORT_TO = 161
BUFFER_SIZE = 4096

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP_LISTEN, UDP_PORT_LISTEN))

cache = []
captured_data = []
captured_data_dic = {}

stub_mode = 'replay'
#stub_mode = 'capture'

if stub_mode == 'capture':
    f = open('capture', 'w')
elif stub_mode == 'replay':
    f = open('capture', 'r')
    captured_data = f.readlines()
    for i in range(len(captured_data)):
        if 'Requested' in captured_data[i]:
           key = urllib.unquote(captured_data[i+3].rstrip())
           value = urllib.unquote(captured_data[i+3+4].rstrip())
           key = key[:17] + 'XXXX' + key[21:]
           captured_data_dic[key] = value
           #print value, key

while True:
    if stub_mode == 'capture':
        # send to remote snmp
        data_request, (host_request, port_request) = sock.recvfrom(BUFFER_SIZE)
        while data_request in cache:
            data_request, (host_request, port_request) = sock.recvfrom(BUFFER_SIZE)
            print 'skipped due to duplicated'
        cache.append(data_request)
        if len(cache) > 10:
            cache = cache[1:]
        print 'Requested:', urllib.quote(data_request), host_request, port_request

        f.write(urllib.quote('Requested data') + '\n')
        f.write(urllib.quote(host_request) + '\n')
        f.write(urllib.quote(str(port_request)) + '\n')
        f.write(urllib.quote(data_request) + '\n')
        sock.sendto(data_request, (UDP_IP_TO, UDP_PORT_TO))

        # receive from remote snmp
        data_response, (host_response, port_response) = sock.recvfrom(BUFFER_SIZE)
        while data_response in cache:
            data_response, (host_response, port_response) = sock.recvfrom(BUFFER_SIZE)
            print 'skipped due to duplicated'
        cache.append(data_response)
        if len(cache) > 10:
            cache = cache[1:]

        f.write(urllib.quote('Responsed data') + '\n')
        f.write(urllib.quote(host_response) + '\n')
        f.write(urllib.quote(str(port_response)) + '\n')
        f.write(urllib.quote(data_response) + '\n')
        f.write('\n')
        sock.sendto(data_response, (UDP_IP_LISTEN, port_request))
        print 'responsed:', urllib.quote(data_response), UDP_IP_LISTEN, port_request
        print '---------------------------'

    elif stub_mode == 'replay':
        data_request, (host_request, port_request) = sock.recvfrom(BUFFER_SIZE)
        while data_request in cache:
          data_request, (host_request, port_request) = sock.recvfrom(BUFFER_SIZE)
          print 'skipped due to duplicated'
        cache.append(data_request)
        if len(cache) > 0:
            cache = cache[1:]
        print 'Requested:', urllib.quote(data_request), host_request, port_request

        request_id = data_request[17:21]

        data_request = data_request[:17] + 'XXXX' + data_request[21:]
        ret_data = captured_data_dic[data_request]
        if ord(ret_data[1]) >= 128:
            length = ord(ret_data[1]) - 128
        else:
            length = 0

        if ord(ret_data[1+length+13]) >= 128:
            length2 = ord(ret_data[1+length+13]) - 128
        else:
            length2 = 0

        pos = 1+length+13+length2+3
        ret_data = ret_data[:pos] + request_id + ret_data[pos+4:]

        sock.sendto(ret_data, (UDP_IP_LISTEN, port_request))
        print 'Responsed:', urllib.quote(ret_data), UDP_IP_LISTEN, port_request
        print '---------------------------'

    else:
        break

共通の解説
解説1
今回は1つのネットワークしか使わないので、ソケットは1つのみ作成。
もしLISTENとTOで分けたいのであれば、それぞれのソケットを作成し、それに合わせたフォワーディングルールを書く必要があります。

capture modeの解説

解説1
処理概要は、1リクエストずつ処理することを想定しています。
つまり、snmpwalkから1リクエストが来たら、本物のsnmp-agentに1リクエストを転送し、
本物のsnmp-agentから応答が来たら、snmpwalkに1リクエストを転送する、を
1パケットずつ処理しています。
snmpwalkのパケットの流れを見たところ、1リクエストずつ処理をしていたので、このような処理方法で問題なさそうです。
ただし、応答が遅いとすぐに再送されてしまうため、再送対策は必要です。

解説2
cacheは、UDP固有のされたパケットを無視する為。
とりあえず、過去10件のパケットを記録し、同じリクエストが来たら再送とみなし
次のリクエストを待つようにしています。

replay modeの解説

解説1
再送対策のコードをここにも入れていますが、実は無効化されています。もし、再送に悩まされたら0を適宜増やしてください。

参考

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6