【※追記 2015/03/05】Software Switch用のKVMゲスト同士をLayer-2で直接接続する（For LLDPの送受信）

##序文

KMV環境において同一ホスト上ののVM～VM間、VM～ホストVeth間の通信には、多くの場合Linux-Bridgeや
Open vSwitchなど、ブリッジデバイスを利用します。
そして多くの場合はこのようなBride経由のVM接続で何ら支障はありません。

ただ、VM上でS/W仮想スイッチを稼働させて、それを複数台に相互接続、LLDPやIGMPの送受信を
させたいような場合、間にブリッジデバイスが介在する事が障害となる場合があります。
（※今回試していないが、IPv6のLink-Localフレームも同様では？）


<br>ときに、私はOpenFlow 1.3対応のスイッチ群でトポロジアウェアな経路制御コントローラを実装しようと
思い立っておりました。
そして、この実装手段の一つにLLDPによるトポロジ検知を検討したのです。

そこで、取り急ぎKVM間を安易にLinux-Bridgeで接続したところLLDPの授受が適いませんでした。

こうした経緯で、KVM間のLayer-2直結（物理で言えば、機器間のイーサネットケーブル直結）の方法を模索し、
その成功結果を共有したいと思った次第です。


<br>手順としては
　・ ホストOS上でのvethペアの作成
　・ 各KVMのホスト側tapデバイスを、上記のvethの片側へmacvtapデバイスとして接続
　・ vethペアデバイス、macvtapデバイスのすべてをUplink状態、Promiscuous許可に設定
この流れで実施し、成功しました。

なお、LLDP受信の確認にはLADVDパッケージのLLDPエージェントを利用しました。

<br>
![macvtap_vs_bridge.png](https://qiita-image-store.s3.amazonaws.com/0/42848/bbcb343e-aa6b-cae7-9383-b5ce43eab7a2.png)


<br>手順の解説に先立って、まずMacvtapデバイスの概要を説明します。


##Macvtapとは

Macvtapデバイスとは、仮想マシン稼働のLinuxカーネルにて、VMとホストOS外部（ホストOSのカーネル以外）との
Layer-2での通信を可能とするために登場した、比較的新しい仮想デバイスです。

上で「ホストOS外部」の言葉で想定しているものは、
　- ホストOSの物理サーバ外部のNW機器
　- 同一のホストOS上で動作する別のVMやNWスタック利用のプロセス
などとなります。


<br>このデバイスTypeは先んじてLinuxカーネルに実装されていたMacvlanデバイスとTapデバイスの2つを組み合わせて、
目的の動作を実現しています。

MacvlanデバイスはLinuxカーネル上の既存NWデバイスに異なるMACアドレスを持つ、新たなLayer-2デバイスを
追加作成する事が出来ます。
そして、この新規生成のデバイスは、追加元のNWデバイスの通信機能を利用して、外部のNWとの通信を行う事が
できるのです。

Macvtapデバイスでは、このMacvlanデバイスを既存デバイス上に生成し、その外部との通信機能を利用します。


<br>次いで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デバイスの通信機能を利用して、外部と入出力します。

<br>参考：
　http://seravo.fi/2012/virtualized-bridged-networking-with-macvtap


##Macvtapの動作モード

Macvtap デバイスの動作モードは4 種類のモードから成っています。
明示指定しない場合、「vepa」がデフォルトのモードとなります。

今回はLink-LocalなLayer-2フレームをKVMゲスト間で送受信する事が目的なので、Bridgeモードは避け、
vepaモードでの接続を実施しました。


他のモードや各モードの仕様に関しては、下に参考のURLを記載します。

参考：
　https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Virtualization_Administration_Guide/sect-attch-nic-physdev.html


##技術的な手順

- ホストOSでのVethペア作成

```text:＠ホストOS
[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として接続

```text:＠ホストOS
[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>
```
![macvtap.png](https://qiita-image-store.s3.amazonaws.com/0/42848/e39b6d58-fe20-b7c9-a780-9470068e1858.png)

<br>

- KVMを起動し、I/FのLinkupとPromiscuous許可を設定

```text:＠ホストOS
[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でのキャプチャ確認

```text:＠KVM①
root@ubu-01:~# apt-get install ladvd
root@ubu-01:~# ladvd -m 192.168.10.2 -n -f -a eth1 &
```

```text:＠KVM②
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
```

<br>
##追記 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コマンド）


![macvtap_malfunc.png](https://qiita-image-store.s3.amazonaws.com/0/42848/91d196d6-ed61-93e3-4a22-317680c0f983.png)

<br>
結局、妥協した迂回策として、Veth直結していた部分をOpen vSwitchとし、そのOVSに2アームのインタフェイス間を
相互に素通しする「仮想パッチパネル」設定を、StaticなOpenFlowルールとして設定する事で、LLDPに加えてICMP等の
通常のEnd-to-End通信も疎通可能な状況を実現できました。

ただ、解決策としてエレガントさを欠くので、継続して他のワークアランドも検討したいと思っています。

![virtpatch.png](https://qiita-image-store.s3.amazonaws.com/0/42848/7213fbe3-5547-ec05-eb8b-3edb81a87bff.png)


```text:仮想パッチパネル用OpenFlow設定＠ホストOS
[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
```