##序文
KMV環境において同一ホスト上ののVM~VM間、VM~ホストVeth間の通信には、多くの場合Linux-Bridgeや
Open vSwitchなど、ブリッジデバイスを利用します。
そして多くの場合はこのようなBride経由のVM接続で何ら支障はありません。
ただ、VM上でS/W仮想スイッチを稼働させて、それを複数台に相互接続、LLDPやIGMPの送受信を
させたいような場合、間にブリッジデバイスが介在する事が障害となる場合があります。
(※今回試していないが、IPv6のLink-Localフレームも同様では?)
ときに、私はOpenFlow 1.3対応のスイッチ群でトポロジアウェアな経路制御コントローラを実装しようと
思い立っておりました。
そして、この実装手段の一つにLLDPによるトポロジ検知を検討したのです。
そこで、取り急ぎKVM間を安易にLinux-Bridgeで接続したところLLDPの授受が適いませんでした。
こうした経緯で、KVM間のLayer-2直結(物理で言えば、機器間のイーサネットケーブル直結)の方法を模索し、
その成功結果を共有したいと思った次第です。
手順としては
・ ホストOS上でのvethペアの作成
・ 各KVMのホスト側tapデバイスを、上記のvethの片側へmacvtapデバイスとして接続
・ vethペアデバイス、macvtapデバイスのすべてをUplink状態、Promiscuous許可に設定
この流れで実施し、成功しました。
なお、LLDP受信の確認にはLADVDパッケージのLLDPエージェントを利用しました。
![macvtap_vs_bridge.png](https://qiita-image-store.s3.amazonaws.com/0/42848/bbcb343e-aa6b-cae7-9383-b5ce43eab7a2.png)
手順の解説に先立って、まずMacvtapデバイスの概要を説明します。
##Macvtapとは
Macvtapデバイスとは、仮想マシン稼働のLinuxカーネルにて、VMとホストOS外部(ホストOSのカーネル以外)との
Layer-2での通信を可能とするために登場した、比較的新しい仮想デバイスです。
上で「ホストOS外部」の言葉で想定しているものは、
- ホストOSの物理サーバ外部のNW機器
- 同一のホストOS上で動作する別のVMやNWスタック利用のプロセス
などとなります。
このデバイスTypeは先んじてLinuxカーネルに実装されていたMacvlanデバイスとTapデバイスの2つを組み合わせて、
目的の動作を実現しています。
MacvlanデバイスはLinuxカーネル上の既存NWデバイスに異なるMACアドレスを持つ、新たなLayer-2デバイスを
追加作成する事が出来ます。
そして、この新規生成のデバイスは、追加元のNWデバイスの通信機能を利用して、外部のNWとの通信を行う事が
できるのです。
Macvtapデバイスでは、このMacvlanデバイスを既存デバイス上に生成し、その外部との通信機能を利用します。
次いでTapデバイスの利用についても説明します。
Tapデバイスは、ホストOSのLinuxカーネル ネットワークスタックと、そのLinux上のプロセスとが
Layer-2レベルのNW通信を行うために利用する仮想NWデバイスです。
すなわち、VMゲストが自身のNICへのR/Wを行うと、その入出力Layer-2フレームは対応するTapデバイスへの
入出力として、ホストOS上でR/Wされる事になります。
Macvtapデバイスでは、ホストOS上に生成されるTapデバイスをMacvlanデバイスとして既存NWデバイスに
アタッチし、ゲストOSの通信フレームを既存のNWデバイスの通信機能を利用して、外部と入出力します。
参考:
http://seravo.fi/2012/virtualized-bridged-networking-with-macvtap
##Macvtapの動作モード
Macvtap デバイスの動作モードは4 種類のモードから成っています。
明示指定しない場合、「vepa」がデフォルトのモードとなります。
今回はLink-LocalなLayer-2フレームをKVMゲスト間で送受信する事が目的なので、Bridgeモードは避け、
vepaモードでの接続を実施しました。
他のモードや各モードの仕様に関しては、下に参考のURLを記載します。
##技術的な手順
- ホストOSでのVethペア作成
[root@fedo21]# ip lin add name veth0_a type veth peer name veth0_b
[root@fedo21]# ip -d link sh dev veth0_a
81: veth0_a: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether de:db:ee:3d:55:ba brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
[root@fedo21]# ip -d link sh dev veth0_b
80: veth0_b: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether b2:6e:02:a8:d4:e2 brd ff:ff:ff:ff:ff:ff promiscuity 2
veth
[root@fedo21]# ethtool -S veth0_a
NIC statistics:
peer_ifindex: 80
- KVMのNICをvethにmacvtapとして接続
[root@fedo21]# virsh list
Id Name State
----------------------------------------------------
14 nfv-ubu-cpqd running
15 nfv-ubu-tedge running
[root@fedo21]# virsh dumpxml 14
<domain type='kvm' id='14'>
<name>nfv-ubu-cpqd</name>
(##### Truncated #####)
</interface>
<interface type='direct'>
<mac address='52:54:00:74:e2:c3'/>
<source dev='veth0_a' mode='vepa'/>
<target dev='macvtap0'/>
<model type='virtio'/>
<alias name='net1'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
</interface>
(##### Truncated #####)
</domain>
- KVMを起動し、I/FのLinkupとPromiscuous許可を設定
[root@fedo21]# ip lin set up veth0_a
[root@fedo21]# ip lin set up veth0_b
[root@fedo21]# ip lin set promisc on veth0_a
[root@fedo21]# ip lin set promisc on veth0_b
[root@fedo21]# ip lin set promisc on macvtap0
[root@fedo21]# ip lin set promisc on macvtap1
- KVMの片割れでLLDPエージェント起動、対向KVMでのキャプチャ確認
root@ubu-01:~# apt-get install ladvd
root@ubu-01:~# ladvd -m 192.168.10.2 -n -f -a eth1 &
root@ubu-02:~# tcpdump -v -i eth1
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
08:46:37.129285 LLDP, length 298
Chassis ID TLV (1), length 7
Subtype MAC address (4): 52:54:00:ab:0d:1a (oui Unknown)
Port ID TLV (2), length 5
Subtype Interface Name (5): eth1
Time to Live TLV (3), length 2: TTL 180s
Port Description TLV (4), length 24: Red Hat, Inc Device 0001
System Name TLV (5), length 7: nfv-ubu
System Description TLV (6), length 93
Ubuntu 14.04.1 LTS Linux 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64
System Capabilities TLV (7), length 4
System Capabilities [Router] (0x0010)
Enabled Capabilities [none] (0x0000)
Management Address TLV (8), length 12
Management Address length 5, AFI IPv4 (1): 192.168.10.2
Interface Index Interface Numbering (2): 3
Organization specific TLV (127), length 6: OUI IEEE 802.3 Private (0x00120f)
Max frame size Subtype (4)
MTU size 1522
Organization specific TLV (127), length 17: OUI ANSI/TIA (0x0012bb)
Inventory - hardware revision Subtype (5)
Hardware revision pc-i440fx-2.1
Organization specific TLV (127), length 26: OUI ANSI/TIA (0x0012bb)
Inventory - firmware revision Subtype (6)
Firmware revision 1.7.5-20140709_153950-
Organization specific TLV (127), length 21: OUI ANSI/TIA (0x0012bb)
Inventory - software revision Subtype (7)
Software revision 3.13.0-32-generic
Organization specific TLV (127), length 8: OUI ANSI/TIA (0x0012bb)
Inventory - manufacturer name Subtype (9)
Manufacturer name QEMU
Organization specific TLV (127), length 36: OUI ANSI/TIA (0x0012bb)
Inventory - model name Subtype (10)
Model name Standard PC (i440FX + PIIX, 1996
End TLV (0), length 0
^C
1 packet captured
1 packet received by filter
0 packets dropped by kernel
##追記 2015/03/05
Vethで直結したKVM間でのLLDP送受信には成功したが、実際にOpenFlowスイッチを検証し始めると、
2台のKVM(それぞれOpenFlowスイッチ)を跨いだDockerコンテナ間の疎通ができない事が判明した。
パケットキャプチャによる切り分けや、Web情報の調査の結果、MacvtapとNICの接続点で、ARPリプライが
中継されない仕様(バグ?)が存在するようです。
以下の図のPing用コンテナ①からPing用コンテナ②へのICMPに先立つARP解決で、①からのリクエストに
②が返したARPリプライが、戻り経路のVeth~Macvtap接続点で中継されない事象が生じました。
なお、以下の策もワークアラウンドとはなりませんでした。
- MacvtapやVethのNamespaceをホストOSのデフォルトと変更する
- 同上のインタフェイスにてマルチキャスト関連のフラグなどをonにする(ip link setコマンド)
結局、妥協した迂回策として、Veth直結していた部分をOpen vSwitchとし、そのOVSに2アームのインタフェイス間を 相互に素通しする「仮想パッチパネル」設定を、StaticなOpenFlowルールとして設定する事で、LLDPに加えてICMP等の 通常のEnd-to-End通信も疎通可能な状況を実現できました。
ただ、解決策としてエレガントさを欠くので、継続して他のワークアランドも検討したいと思っています。
[root@fedo21]# ovs-ofctl add-flow ovbr0 in_port=1,action=output:2
[root@fedo21]# ovs-ofctl add-flow ovbr0 in_port=2,action=output:1
[root@localhost ~fedo21]# ovs-ofctl dump-flows ovbr0
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=284.702s, table=0, n_packets=9, n_bytes=2806, idle_age=6, priority=0 actions=NORMAL
cookie=0x0, duration=68.381s, table=0, n_packets=0, n_bytes=0, idle_age=68, in_port=1 actions=output:2
cookie=0x0, duration=2.384s, table=0, n_packets=0, n_bytes=0, idle_age=2, in_port=2 actions=output:1