GNS3 環境で、仮想マシンを外部接続させる際の注意点

# はじめに
GNS3のラボネットワーク用に、KVM 上で SYSLOG サーバとか Linux サーバ を立てたい時がある。各サーバで必要なパッケージは apt で引っ張ってくるのだけれど、それがめちゃくちゃに遅い。なんだよ800 Bpsって。 サーバの再初期構築をした後のパッケージ更新だけで1日かかる。調べると GNS3 的には FAQ だった。事象回避のためのメモ

## 前提条件
- GNS3 Version 2.2.3
- GNS3 サーバの動作環境 Ubuntu 18.04

GNS3サーバはリモートで動作させた。構築方法は[この記事を参照](https://qiita.com/Shakapon/items/0ddce186213521a42660).

## なにが悪いのか？
<img width="941" alt="GNS3_Cloud接続1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409463/ef3b664d-9c9e-a473-ceee-25eea3754d9a.png">

GNS3に配置した仮想マシンを Internetに接続する際は Cloud と接続する。Cloud の持つ Interface は GNS3 VMが持つ Interfaceとなるのだが、**Cloud Node で物理デバイスと接続すると死ぬほど遅くなる**問題が報告されている。[2011年12月には記事になっている](http://myhomelab.blogspot.com/2011/12/add-loopbacks-in-ubuntu-for-gns3.html)くらいの既知の問題。原因について思い当たる節は色々あるけれど、明確な回答は見つけられず。最終的にはソースコード読むしかなさそう。

## 回避策は？
[この投稿](https://github.com/GNS3/gns3-gui/issues/1921#issuecomment-288530478)によると手は二つ

1. TAP を直接接続する**L3接続**. 
    1. GNS3 サーバで TAP Interface (TAP0; Loopback Interface) を作って/30のアドレスを割り当てる。
    2. GNS3 サーバで Routing設定
    3. GNS3 上で新しく cloud Node を配置し、**TAP インターフェース**を仮想マシンと接続

2. TAP を Bridge 経由で接続する**L2接続**.
   1. GNS3 サーバで TAP Interfaceを割り当て(e.g. TAP0)、Promiscuousモードにする
   2. GNS3 サーバで外部接続に用いていたInterface(e.g. ens160) からIPアドレスを削除し、Promiscuousモードにする
   3. GNS3 サーバで新しく Bridgeを生成(e.g. br0)し、TAP0/ens160を所属させる。
   4. GNS3 サーバ自体が外部接続できるよう、Bridge へ新たに IP アドレスを割り当てる（ens160で割り当ててた奴）
   5. GNS3 上で新しく cloud Node を配置し、**TAP インターフェース**を仮想マシンと接続
  
この資料では後者のL2接続とした。

# 手順
## 動作確認
Ubuntu での Network 設定が ifconfig から ip へ変わっているため、下記のようにする。

```console
$ sudo modprobe tun
$ sudo tunctl -u gns3 -t tap0
$ sudo ip link set tap0 promisc on
$ sudo ip link set ens160 promisc on
$ sudo brctl addbr br0
$ sudo brctl addif br0 tap0
$ sudo brctl addif br0 ens160
$ sudo ip addr add 192.168.1.11/24 dev br0
$ sudo ip link set br0 up
```

で、動作確認。

```console
$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.f2735e6c8b7f       no              ens160
                                                        tap0
$ ip addr show br0
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether f2:73:5e:6c:8b:7f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.11/24 brd 192.168.1.255 scope global br0
       valid_lft forever preferred_lft forever
```

GNS3 上で新しく Cloud Node を配備し、そのTAP0を仮想サーバと接続すればよい。 
でもこのままだと GNS3 サーバをリブートすると初期化されてしまうので注意。

## 永続化
今回GNS3 Serverを構築したUbuntu 18.04での Network 設定は NetPlan で行う。ただ、PROMISCUOUS MODE の設定は[NetPlanでできない](https://qiita.com/Shakapon/items/0eea9f7492d0868c5cb1) などの制限が存在する。NetPlan でできないことは Network-Dispatcher でカバーする必要がある。 

### Network-Dispatcher

Network-Dispatcher で起動時のhookで設定を読み込ませるためには`/etc/networkd-dispatcher/routable.d/` 配下に下記のようなスクリプトを書く必要がある。`routable.d`以外にもディレクトリはあるが、それぞれのディレクトリ配下のスクリプトが実行されるタイミングは[ここを参照](https://netplan.io/faq#use-pre-up-post-up-etc-hook-scripts)のこと。

```sh:/etc/networkd-dispatcher/routable.d/50-ifup-hooks
#!/bin/sh

modprobe tun
tunctl -u gns3 -t tap0
ip link set tap0 promisc on
ip link set ens160 promisc on
```

作成したら実行権限を忘れずに付与。

```console
$ sudo chmod 755 /etc/networkd-dispatcher/routable.d/50-ifup-hooks
```

### NetPlan
起動時に読み込まれるyamlファイルを下記のように作成。作成した後`sudo netplan apply `すれば設定は即時反映される。

```yaml:/etc/netplan/01-network-manager-all.yaml
# This file is configured for GNS3 Server
network:
  version: 2
  renderer: networkd
  ethernets:
    tap0:
      dhcp4: no
    ens160:
      dhcp4: no

  bridges:
    br0:
      addresses:
        - 192.168.1.11/24
      gateway4: 192.168.1.255
      nameservers:
        addresses:
          - 192.168.1.1
      interfaces: [ens160,tap0]
```