UDPポートフォワーディング

# はじめに

TCPのポートフォワーディングはよく見かけるのですが、UDPのポートフォワーディングは用途が限られていることもあるのか、あまり見かけません。
今回は、UDPやソケットの勉強を兼ねてUDPのポートフォワーディングをしたいと思います。

# 環境
OS X El Capitan
Python 2.7.11
snmpwalk 5.6.2.1

# Pythonで実装する

```lang:udp_portforwarder.py
import socket
UDP_IP_LISTEN = "192.168.1.12"
UDP_PORT_LISTEN = 161
UDP_IP_TO = "192.168.1.119"
UDP_PORT_TO = 161

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

src_port = 0

while True:
  data, (host, port) = sock.recvfrom(4096)

  # receive from remote server
  if src_port != 0 and host != UDP_IP_LISTEN:
    sock.sendto(data, (UDP_IP_LISTEN, src_port))
  # send to remote server
  else:
    src_port = port
    sock.sendto(data, (UDP_IP_TO, UDP_PORT_TO))
   
  print 'forwarded', host, port
```

解説1
UDPを対象にしたいので、socketにはSOCK_DGRAMを指定。

解説2
bindしたポートには、要求元からと、送付先からの応答と2種類のパケットがrecvfromで届くので、送信元アドレスで条件分岐。

解説3
要求元のポートに返してあげる必要があるので、要求元からのパケットが到着時に要求元ポート番号をsrc_portに格納。

# 動作確認

1. tshark udpを実行（パケットの詳細を確認する為）
1. sudo python udp_capture_portforwarder_remote.pyを実行
1. snmpwalk -c public -v 1 192.168.1.12を実行

# 動作結果

```lang:snmpwalk
snmpwalk -c public -v 1 192.168.1.12
butada-mac:python_udp_capture_test butada$ 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: (454525) 1:15:45.25
SNMPv2-MIB::sysContact.0 = STRING: SysAdmin
SNMPv2-MIB::sysName.0 = STRING: SystemName
SNMPv2-MIB::sysLocation.0 = STRING: data centre A
SNMPv2-MIB::sysORLastChange.0 = Timeticks: (4) 0:00:00.04
SNMPv2-MIB::sysORID.1 = OID: SNMP-FRAMEWORK-MIB::snmpFrameworkMIBC
＜省略＞
```

```lang:udp_capture_portforwarder_remote.py 
$ sudo python udp_capture_portforwarder_remote.py 
sent to remote snmp
forwarded 192.168.1.12 57598
received from remote snmp
forwarded 192.168.1.119 161
sent to remote snmp
forwarded 192.168.1.12 57598
received from remote snmp
forwarded 192.168.1.119 161
sent to remote snmp
forwarded 192.168.1.12 57598
received from remote snmp
forwarded 192.168.1.119 161
sent to remote snmp
forwarded 192.168.1.12 57598
received from remote snmp
forwarded 192.168.1.119 161
sent to remote snmp
forwarded 192.168.1.12 57598
received from remote snmp
forwarded 192.168.1.119 161
＜省略＞
```

```lang:tshark
$ tshark udp
1897 2373.448956 192.168.1.12 -> 192.168.1.119 SNMP 82 get-next-request 1.3.6.1.2.1
1898 2373.450958 192.168.1.119 -> 192.168.1.12 SNMP 215 get-response 1.3.6.1.2.1.1.1.0
1899 2373.451207 192.168.1.12 -> 192.168.1.119 SNMP 85 get-next-request 1.3.6.1.2.1.1.1.0
1900 2373.453134 192.168.1.119 -> 192.168.1.12 SNMP 95 get-response 1.3.6.1.2.1.1.2.0
1901 2373.453259 192.168.1.12 -> 192.168.1.119 SNMP 85 get-next-request 1.3.6.1.2.1.1.2.0
1902 2373.454931 192.168.1.119 -> 192.168.1.12 SNMP 88 get-response 1.3.6.1.2.1.1.3.0
1903 2373.455061 192.168.1.12 -> 192.168.1.119 SNMP 85 get-next-request 1.3.6.1.2.1.1.3.0
1904 2373.456796 192.168.1.119 -> 192.168.1.12 SNMP 93 get-response 1.3.6.1.2.1.1.4.0
＜省略＞
```

# 結果

snmpgetを用いて、UDPのポートフォワーディングができることを確認しました。具体的な結果をここに載せられませんでしたが、IPMIについてもipmiutilを使って同様のポートフォワーディングできることを確認できました。

# 応用

## SNMPのパケットキャプチャ＆リプレイ
このテクニックを応用して、パケットのキャプチャや、簡易的なスタブを作りました。つまり、フォワーディングする際に記録するのがキャプチャで、そのキャプチャ内容に基づいてフォーワーディングせずに記録した応答を返すのがスタブになります。
http://qiita.com/butada/items/77af324c2a1bb880f101

# 参考 

http://memo.saitodev.com/home/python_network_programing/
http://stackoverflow.com/questions/11350145/are-there-simple-descriptions-on-port-forwarding-using-python
http://stackoverflow.com/questions/2694212/socket-set-source-port-number