本實作是使用 Ryu NAT (https://github.com/John-Lin/nat)來完成 NAT 的行為進而來了解 NAT 運作。
實驗拓樸
先從Github下載NAT Code
$ gitclone
https://github.com/John-Lin/nat
$ cd nat
首先因為在實驗環境的不同,我們還需要在snat.py設定參數
$ iperf -s
根據自己的架設環境來設定相關的參數
接著啟動Ryu NAT
$ ryu-manager base.py l2switch.py dhcp.py snat.py --verbose
試著從 Host 1 對 Host 2 個別執行 Ping 以及Iperf 指令
Ping
H1:
$ ping 192.168.1.10
Iperf
H2:
H1:
$ iperf -c 192.168.1.10
可以發現 Ping 無法通過,但是 Iperf ( TCP ) 的協定卻通過了
再來由 Wireshark 觀察 H1 發送 Ping 以及 Iperf 指令給 H2 時,所發出的封包以及接受到的封包
Ping:
H1:
H2:
Iperf:
H1:
H2:
經過了比較可以發現,在使用Ping指令 H1 發送到的封包經過 NAT 後,卻沒有更改到 Source 為NAT的Public IP,但是 TCP 卻有更改到,所以可以合理的懷疑是否ICMP的部分是不是沒有去解析到。
在 snat.py 中處裡 Packet In 程式碼
這裡可以清楚的看到作者有想要只去做獲得ICMP封包資料,但是卻沒有去完成。
if pkt_tcp:
# print "@@@ Install TCP Flow Entry @@@"
tcp_src = pkt_tcp.src_port
tcp_dst = pkt_tcp.dst_port
match = parser.OFPMatch(in_port=in_port,
eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_TCP,
ipv4_src=ipv4_src,
ipv4_dst=ipv4_dst,
tcp_src=tcp_src,
tcp_dst=tcp_dst)
actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]),
parser.OFPActionSetField(ipv4_src=self.nat_public_ip),
parser.OFPActionSetField(tcp_src=nat_port),
parser.OFPActionOutput(out_port)]
match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_TCP,
ipv4_src=ipv4_dst,
ipv4_dst=self.nat_public_ip,
tcp_src=tcp_dst,
tcp_dst=nat_port)
actions_back = [parser.OFPActionSetField(eth_dst=eth_src),
parser.OFPActionSetField(ipv4_dst=ipv4_src),
parser.OFPActionSetField(tcp_dst=tcp_src),
parser.OFPActionOutput(in_port)]
elif pkt_udp:
# print "@@@ Install UDP Flow Entry @@@"
udp_src = pkt_udp.src_port
udp_dst = pkt_udp.dst_port
match = parser.OFPMatch(in_port=in_port,
eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_UDP,
ipv4_src=ipv4_src,
ipv4_dst=ipv4_dst,
udp_src=udp_src,
udp_dst=udp_dst)
actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]),
parser.OFPActionSetField(ipv4_src=self.nat_public_ip),
parser.OFPActionSetField(udp_src=nat_port),
parser.OFPActionOutput(out_port)]
match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_UDP,
ipv4_src=ipv4_dst,
ipv4_dst=self.nat_public_ip,
udp_src=udp_dst,
udp_dst=nat_port)
actions_back = [parser.OFPActionSetField(eth_dst=eth_src),
parser.OFPActionSetField(ipv4_dst=ipv4_src),
parser.OFPActionSetField(udp_dst=udp_src),
parser.OFPActionOutput(in_port)]
else:
pass
所以在下Flow Entry的指令時,也都沒有ICMP的部分
因此如果我們需要使Ping指令有效的通過,就必須再增加ICMP部分的解析
增加ICMP部分
所以第一步先得到 ICMP 的標頭資訊
pkt_icmp = pkt.get_protocol(icmp.icmp)
再來是處裡當確定是ICMP封包時,需要下甚麼指令給交換器。
elif pkt_icmp:
print "@@@ Install ICMP Flow Entry @@@"
match = parser.OFPMatch(in_port=in_port,
eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_ICMP,
ipv4_src=ipv4_src,
ipv4_dst=ipv4_dst,
)
actions = [parser.OFPActionSetField(eth_dst=IP_TO_MAC_TABLE[target_ip]),
parser.OFPActionSetField(ipv4_src=self.nat_public_ip),
parser.OFPActionOutput(out_port)]
match_back = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP,
ip_proto=inet.IPPROTO_ICMP,
ipv4_src=ipv4_dst,
ipv4_dst=self.nat_public_ip,
)
actions_back = [parser.OFPActionSetField(eth_dst=eth_src),
parser.OFPActionSetField(ipv4_dst=ipv4_src),
parser.OFPActionOutput(in_port)]
else:
pass
再來還要判斷目標位址是否有在 ARP TABLE
之後再啟動 Ryu NAT
$ ryu-manager base.py l2switch.py dhcp.py snat.py --verbose
再試著由 H1 對 H2 使用 Ping 指令
H1:
$ ping 192.168.1.10
可以發現這次,會通過了
H1:
H2: 也有正確收到 H1 經過 NAT 後更改過的封包