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情報を定義しているけど、表にするとこうなる。

lhsrhs
segmentrealvirtualsegmentrealvirtual
ipaddripaddrhwaddripaddripaddrhwaddr
network1192.168.11.90192.168.11.8554:52:00:13:00:05 network3192.168.11.91192.168.11.8154:52:00:31:00:01
network3192.168.11.91192.168.11.8354:52:00:32:00:03 network2192.168.11.91192.168.11.8554:52:00:23:00:05
network2192.168.11.90192.168.11.8154:52:00:21:00:01 network1192.168.11.90192.168.11.8254:52:00:12:00:02

表見ると分かりますが、

  • 対向実ホストが同一IPアドレス
  • 対向仮想IPが同一IPアドレス
  • 複数の同一ネットワークアドレスのセグメント

とか、普通はやれない(やれてもやらないだろうけど)という設定にしてあります。

一番上のエントリで動作を説明すると、以下のようになります。

  • 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 させるフローを縛ってやればいいんだろうけど。

課題

  • 実ホストの MAC アドレスを固定してしまうなら、コンフィグに real の hwaddr も記載してしまえばいい。こうするとほぼ L2/L3 の条件を指定したインテリジェントパッチパネルになるので、プロアクティブにルールを書き込んでしまえばいい。
    • 今回は実ホストの ARP で動的に解決するようにしている。ただ、対向側の実ホストの ARP が解決できない状況を想定しておらず、仮想IPに対する ARP Request がきた段階で reply を返してしまうと言う proxy arp 的な動作をしてしまう。この辺の動作は本当にこれでいいのか…とかは考える必要がありそう。
  • 仮想 MAC は今回一意に設定した。これは、今は設定ファイル上で記載しているが別に自動生成でも良い。
    • 機構的には仮想 MAC が重複していても別にかまわないが(原理的にはセグメント内で一意のアドレスになっていれば良いので)、これをやると非常にデバッグが面倒。面倒というかものすごくやりにくい。
    • まあ、Src/Dst NAT は OpenFlow だろうが通常のルータ類で実装しようが、その行為自体が本質的に複雑で非常に管理しにくいのであんまりやりたくはないねえ…。