Trema/P2PSrcDstNatSwitch
はじめに
インフルエンザにつぶれた1週間。Janogは申し込みしておいたものの出席できるわけもなく(インフル治療中にいったらバイオテロだろ…)、家で Ust を見るだけでした…。初日午前のプログラム 監視網設計/運用の苦労あれこれ | JANOG31 Meeting の NAT の話とかで、「OpenFlowでの実装も考えてはみたものの…」と言う話が出ていたので、やってみたらどうなるのかちょっと試してみた。
構成
Trema/SimpleL3Switch(1) で作った物をいじる形で。
どうせやるなら OpenFlow でやるからこそできる物を試してみるか…、ということで
- 3つ、セグメントを切る
- どのセグメントも同じネットワークアドレスを使う
- 同じIPアドレスを使っているホスト同士で通信させる
というのを試してみました。コードは以下。
NATテーブル
まず NAT 情報として。p2psdnatswitch.conf.yml に
:nat_table: - lhs: segment: "network1" real: ipaddr: "192.168.11.90" virtual: hwaddr: "54:52:00:13:00:05" ipaddr: "192.168.11.85" rhs: segment: "network3" real: ipaddr: "192.168.11.91" virtual: hwaddr: "54:52:00:31:00:01" ipaddr: "192.168.11.81"
というような感じでNAT情報を定義しているけど、表にするとこうなる。
lhs | rhs | ||||||
---|---|---|---|---|---|---|---|
segment | real | virtual | segment | real | virtual | ||
ipaddr | ipaddr | hwaddr | ipaddr | ipaddr | hwaddr | ||
network1 | 192.168.11.90 | 192.168.11.85 | 54:52:00:13:00:05 | network3 | 192.168.11.91 | 192.168.11.81 | 54:52:00:31:00:01 |
network3 | 192.168.11.91 | 192.168.11.83 | 54:52:00:32:00:03 | network2 | 192.168.11.91 | 192.168.11.85 | 54:52:00:23:00:05 |
network2 | 192.168.11.90 | 192.168.11.81 | 54:52:00:21:00:01 | network1 | 192.168.11.90 | 192.168.11.82 | 54:52:00:12:00:02 |
表見ると分かりますが、
とか、普通はやれない(やれてもやらないだろうけど)という設定にしてあります。
- Controller 側で同じセグメントに仮想IP/MACを設定。対向の接続先と1対1に対応させる。
- 仮想IP/MACに対する通信は、Src/Dst MAC/IPを書き換えて対向側ホストに送る。
- 各ホストは同一セグメント内の通信をしているだけに見える。仮想IPとの通信は実際にはNATされた別セグメントのホストとの通信になる。
今回、実ホスト(real)の MAC アドレスについてはコントローラ側で ARP による MAC アドレス解決をさせる。これは Simple Router とかでやってる処理をそのまま流用。
主要な処理
IPアドレスの重複
セグメント間で使用するIPアドレスが重複することを想定すると、何らかの情報を付加して内部的には一意になるように取り扱う必要がある。単純に、Segment + IP Address という形で、どのセグメントのIPアドレスなのか、というのを内部的には記録して、かならず segment + address で検索・マッチングしてやる必要がある。
- Arp Table
- Nat Table
アドレス変換処理
def handle_ipv4(dpid, message) segment = segment_name_of message.in_port nat_record = @nat_table.find_by_segment_and_vipaddr(segment, message.ipv4_daddr) if nat_record nrc = nat_record[:counter] # to get real hwaddr and port (counter/real) arp_entry = @arp_table.lookup_by_segment_and_ipaddr(nrc.segment, nrc.real_ipaddr) if arp_entry actions = [ SetEthSrcAddr.new(nrc.virt_hwaddr.to_s), SetEthDstAddr.new(arp_entry.hwaddr.to_s), SetIpSrcAddr.new(nrc.virt_ipaddr.to_s), SetIpDstAddr.new(nrc.real_ipaddr.to_s), SendOutPort.new(arp_entry.port) ] flow_mod dpid, message, actions packet_out dpid, message.data, actions else # handle_unresolved_packet handle_unresolved_packet dpid, message, nrc, nrc.real_ipaddr end else # no_op puts "handle_unresolved_packet: __TBD__" end end
見たままですが、NAT Table から対向側(nat_record[:counter]
)の情報を元に、IP/MACの付け替えをやってやるだけ。
動作
dump-flows してみる。
root@oftest01:~# ovs-ofctl dump-flows br0 NXST_FLOW reply (xid=0x4): (略) cookie=0xb, duration=4391.834s, table=0, n_packets=5, n_bytes=490, priority=65535,icmp,in_port=6,vlan_tci=0x0000,dl_src=54:52:00:00:00:05,dl_dst=54:52:00:31:00:01,nw_src=192.168.11.91,nw_dst=192.168.11.81,nw_tos=0,icmp_type=8,icmp_code=0 actions=mod_dl_src:54:52:00:13:00:05,mod_dl_dst:54:52:00:00:00:01,mod_nw_src:192.168.11.85,mod_nw_dst:192.168.11.90,output:2
仮想IPへの通信は、NAT Tableで設定した対向同士でないと成り立たない。仮想IP自体は同一セグメントにおいてあるだけなので、対向として設定されていないホストからもリクエストが送れるが、Src/Dst の双方を書き換えた時点で送信者(返信先)情報が消えるため、以下のように request/reply のパスがずれた状態になる。
まあ、match の書き方次第でちゃんと対向の実ホスト同士に NAT させるフローを縛ってやればいいんだろうけど。