OpenFlow tunnel extension
on Lagopus vswitch
Dec 16, 2015
Masaru OKI @masaru0714
OpenFlow General Tunnel Support
OpenFlow 1.4に向けて提案された拡張
● actionにencap, decapを追加し、フローエントリを使ってトンネリングを実現
● gre protocol, gre key, vxlen vniなどのmatchとset-fieldを追加
● 実装されないと仕様に入らない
→入らないまま現在に至る
● OpenFlow 1.5で定数がかぶっていて、reviseが必要
● LagopusはOpenFlow 1.3用に作ってあるので、ひとまずそのまま適用してみる
○ トンネル対象はGRE, MPLS PW, VXLAN
decap, encap
例としてGRE tunnelのケース。push, popに似ている。
ethernet IP GRE tunneled ethrenet packet
ethrenet packet
decap ether
decap ip
decap gre
encap gre
encap ip
encap ether
ofp_action_decap
struct ofp_action_decap {
uint16_t type;
uint16_t len;
uint32_t cur_pkt_type;
uint32_t new_pkt_type:
uint8_t pad[4];
};
decap前のパケット種別。
validate用と定義されているが、 decap対象と考える。
(OFPHTN_ONF<<16) | OFPHTO_ETHERNET
→ Ethernetヘッダをdecapする。
(OFPHTN_ETHERTYPE<<16) | ETHERTYPE_IP
→ IPv4ヘッダをdecapする。
(OFPHTN_IPPROTO<<16) | IPPROTO_GRE
→ GREヘッダをdecapする。
decap後のパケット種別。
decap後のpayloadをどう解釈するかの指定。
例:GREをdecapしたあとがL2なのかIPなのかを指定する。
(OFPHTN_ONF<<16) | OFPHTO_USE_NEXT_PROTO
→ decap前のヘッダに書かれたプロトコル情報を使う
ofp_action_encap
struct ofp_action_encap {
uint16_t type;
uint16_t len;
uint32_t packet_type;
struct ofp_ed_prop_header props[0];
};
encap後のパケット種別。
(OFPHTN_IPPROTO<<16) | IPPROTO_GRE
→ GREヘッダをencapする。
IPv4, Etherなどdecapと同様に順にencapする。
CRCなどは適宜計算する。
encapに伴う属性を追加で指定できる。
OFPPPT_PROP_PORT_NAMEを指定すると、
フローエントリの投入タイミングで logical portを作成。
統計情報の取得ができるようになる。
decap, encapとpacket_type
decap, encapすると非Ethernetのパケット構造に変化する。
ethernet IP GRE tunneled ethrenet packet
ethrenet packet
IP GRE tunneled ethrenet packet
GRE tunneled ethrenet packet
OFPHTN_ONF
OFPHTO_ETHERNET
OFPHTN_ETHERTYPE
ETHERTYPE_IP
OFPHTN_IP_PROTO
IPPROTO_GRE
OFPHTN_ONF
OFPHTO_ETHERNET
拡張されたmatch field
ここでも一例としてGREのものを並べる。IPPROTO_GREのときのみmatchできる。
● OFPXMT_OFB_GRE_FLAGS
● OFPXMT_OFB_GRE_VER
● OFPXMT_OFB_GRE_PROTOCOL
● OFPXMT_OFB_GRE_KEY
● OFPXMT_OFB_GRE_SEQNUM
matchおよびset-fieldで指定できる。keyはmaskつきmatchが可能。
GRE header
● RFC2890(ver0)では、checksum, key, sequence等はoptional
● RFC2637 (ver1)PPTP用拡張では、checksumなし、keyは必須でlengthとidに分割
C言語風に書くと、
struct {
uint16_t flags;
uint16_t ptype;
uint32_t key;
uint32_t seq_num;
uint32_t ack_num;
};
flagsによって省略されてるかがわかる。
それによりヘッダサイズが変わる。
ver0: 32bit key
ver1: 上位16bitがlengthで下位16bitがcall ID
下位3bitがバージョン
Lagopus dataplaneの実装状況
● encap
○ ether, ipv4, udp, vxlan, gre, mpls
● decap
○ ether, ipv4, udp, vxlan, gre, mpls
● match field
○ gre_flags, gre_ver, gre_protocol, gre_key, vxlan_flags, vxlan_vni (IPv4,UDP,MPLSは既存のみ)
● set-field
○ gre_flags, gre_ver, gre_protocol, gre_key, vxlan_flags, vxlan_vni (IPv4,UDP,MPLSは既存のみ)
GRE,VXLAN,MPLS PWならtunnelできるようになった!
と言えそうですが課題が山積み。
encapの課題(GREの場合)
● seq_numをつけるか省くかが、flags次第
○ encap実行時にはわからない
○ わからないとヘッダサイズが不明なので encapできない
○ encapするまえにset-field gre_flagsすることはできない
● ver=1ならchecksumは省くが、0の場合つけるか省くかはflags次第
○ encap実行時には以下同文
● ver=0の場合keyを省くこともできるがflags次第
○ 以下同文
● Linux gretapではver=0でkeyあり
● 解決案
○ 案1: key必須, seq_numなしで固定とする。
○ 案2: encapにflagsを渡せるような拡張を施す ?
○ 案3: packet_typeをver別に細かく規定する ?
set-fieldの課題(GREの場合)
● keyは4バイトとなっている。
● ver=1の拡張GREでは、keyの上位16bitはpayload lengthとなっている
● set-fieldで4バイト書くためにpayload lenghtを知る必要があるがOFではできない
● maskつきset-fieldはOpenFlow 1.5で拡張されている。
● LagopusはOpenFlow 1.3用として作っていて、まだ対応していない。
○ 案1: がんばってmaskつきset-fieldに対応する
○ 案2: 2byte keyのset-fieldをできるような(EXT-382にはない)拡張を施す
● ただしLinux gretapではver=0のためmaskつきset-fieldは未使用
encapの課題(GREに限らず)
● encap後のipv4_srcとeth_srcは?
○ eth_srcはNICに紐づくが、outputするまではどのportを使うか不明。いつ、だれが埋める ?
■ 案1: コントローラがport_statsしてencapのフローエントリ投入前に把握する
■ 案2: Lagopusが勝手に埋める(set-fieldされていたら勝手に上書きする orz)
■ 案3: 「特定ポートのMAC」をcopy-fieldする(OF1.3にはないactionですが)
■ 案4: 事前に調べておいた値でフローエントリを作成する (ipv4_srcもこれでいける)
○ arpは誰がどうやって答える ? (デモ程度なら静的でもよい )
■ 案1: コントローラががんばる
■ 案2: Linux kernelにぶん投げてお任せ (hybrid有効にするとtap経由で送受信できる )
■ 案3: register等を駆使してOFでがんばる(OF1.3ではできませんが……)
● encap後のipv4_dstとeth_dstは?
○ ipv4_dstはトンネルの接続先なので NW設計時に決まる。staticにset-fieldしてもらっていい。
○ eth_dstは通常NWではgateway ipからarpしてMACを得る。さて……?
■ Linux kernelがarpするならその結果をどうにかして拾えれば ……
decapの課題(GREに限らず)
● GREやVXLANだからといって全部decapしていいわけじゃない
● 自分宛でないパケットをdecapするのはまずい
● 自分宛であることをOpenFlowでどうやって調べることができる? (できない)
○ 案1: コントローラがport情報からMACを得て、matchするもののみdecapするフローエントリを作る
○ 案2: 「自分のMACアドレス」を意味する match fieldを増やす
○ 案3: packet_typeに自分宛であるか示すフラグを増やす
○ 案4: 事前にNW設計した自分のIP宛のみdecapするフローエントリを作る
Lagopus dataplane Tunnel実装の制約 (Dec 2015)
● decap
○ GREは拡張GRE(ver=1)かつseqなし固定、MPLS PWはCW/ACH未対応
● encap
○ GREは拡張GRE(ver=1)かつseqなし固定、MPLS PWはCW/ACH未対応
○ logical port作成には未対応
○ ipv4_dst, eth_dstはケアしないので静的に設定するかコントローラががんばる
● match
○ 自ホスト宛を示すmatchはないので、全部受けるか静的かコントローラががんばる
● set-field
○ gre keyはmaskつきset-fieldに対応させた
● その他全体的に
○ まだチェックが甘いので想定外のパケットを受け取ると死ぬ可能性あり
Tunnel実装の動作デモ環境
2ポート接続、片方をGRE tunnelにする。1ホストで実現するためnetnsを活用。
Host
Lagopus
NET0
NET1
GRE
172.21.1.1/24
172.21.1.2/24
veth1
10.1.0.1
veth0
10.1.0.2
veth2 veth3
Tunnelデモ: Linux側設定
Network NamespaceにそれぞれI/Fを割り振り、片方にGREを設定。
sudo ip netns add NET0
sudo ip netns add NET1
sudo ip link set veth1 netns NET0
sudo ip link set veth3 netns NET1
sudi ip netns exec NET0 arp -i veth1 -s 10.1.0.2 xx:xx:xx:xx:xx:xx pub
sudo ip netns exec NET0 ip addr add 10.1.0.1/24 dev veth1
sudo ip netns exec NET0 ip link add gr0 type gretap key 1 local 10.1.0.1 remote 10.1.0.2
sudo ip netns exec NET0 ip addr add 172.21.1.1/24 dev gr0
sudo ip netns exec NET1 ip addr add 172.21.1.2/24 dev veth3
sudo ip netns exec NET0 ping 172.21.1.2で応答がないことを確認
Tunnelデモ: Lagopus側設定
lagoshから流し込むlagopus.confはごくふつうの2ポートブリッジ。rawsock版。
lagosh --dsl-encodeでDSL形式にする。
channel ch { dst-addr 127.0.0.1; }
controller ctl { channel ch; }
interface i0 { type ethernet-rawsock; device veth0; }
interface i1 { type ethernet-rawsock; device veth2; }
port p1 { interface i0; }
port p2 { interface i1; }
bridge br0 { controller ctl; port p1 1; port p2 2; }
Tunnelデモ: フローエントリ
2ポート間のパケットを相互転送、ただし片方はGREトンネルを経由する。
下記内容をRyuアプリケーションとして記述する。
in_port=1,
eth_type=ipv4,ipv4_src=10.1.0.1,ipv4_dst=10.1.0.2,ip_proto=gre,gre_key=1,
apply_actions=decap(ether,ip),decap(ip,gre),decap(gre,next),
output:2
in_port=2,
apply_actions=encap(gre),set-field:1->gre_key,
encap(ip),set-field:10.1.0.2->ipv4_src,set-field:10.1.0.1->ipv4_dst,
encap(ether),set-field:xx:xx:xx:xx:xx:xx->eth_src,set-field:xx:xx:xx:xx:xx:xx->eth_dst,
output:1
Tunnelデモ
lagopus起動
sudo lagopus -C ./tunnel.dsl
Ryu起動
ryu-manager ./tunnel-demo.py
pingは通る?
sudo ip netns exec NET1 ping 172.21.1.1
sudo ip netns exec NET0 ping 172.21.1.2
GREトンネル越しのPingが通った!! (Dec 16, 16:35JST)
ひっかかった点
● veth0(lagopus側GRE)のMACアドレスをLinuxに覚えてもらう方法
○ 今回はveth0に10.1.0.2/24を振って、カーネルに ARP返答してもらいました
● フローエントリのMACアドレス(veth0, veth1ともに)
○ 今回は調べて直接書きました
● GREのverとかkeyとか
○ 合わないと当然通信できない。 set-fieldできるがデフォルトもなるだけ揃えた
● encapしたIPヘッダのsum
○ 合っていないとLinux側で捨てられる
○ encapで内部管理のethertypeを設定忘れ、チェックサム再計算が動いてなかった
○ 受け側でnetstat -w -sするとwith invalid headersの数字が増えていたため判明

Open flow tunnel extension on lagopus vswitch