AndroidスマートフォンでOpen vSwitch+Tremaを動かす


# 概要

2015/08/08に行われた[TremaDay](http://trema.connpass.com/event/16844/)で発表したものの改善内容。
当日は[OpenVNet](http://openvnet.org/)を動かそうと思ったがその前のruby+c版のtremaのarm移植を中途半端にしてしまったため失敗。
そこで、今回はpure ruby版tremaを動かすところまでの手順にする。

# 意味

この内容に一体どういう意味があるか。
以下の考えに基づいて実行している。

* 今現在、自分の欲しいあらゆる機能を集約し、かつ可搬性の高いインフラは、今で言うところのスマートフォンである
  * Linuxが動く
  * 小さい（みんな持ってる）
  * 軽い
  * 消費電力が非常に小さい
  * デフォルトでUPS（バッテリー）が付いている
  * 電話ができる（次世代インフラ！）
* これを全力で並べて、次世代データセンターにすべき

Nexus等、海外端末を利用すればもっと簡単だが、学ぶことが多そうな、難し目の国産端末で実施。

# 実施

## 準備するもの

* [SHARP SH-03C](https://ja.wikipedia.org/wiki/SH-03C)
* 通信用MicroUSBケーブル
* MicroSD(8GBでもいいが、できれば16GBくらいあったほうがいい)
  * 速いほうが多分いい
* [armのubuntuイメージ](http://cdimage.ubuntu.com/releases/12.04.5/release/ubuntu-12.04-preinstalled-desktop-armhf+ac100.tar.gz)
  * toolchainを作るのが辛いのでandroid上でubuntuのコマンド群を利用する
* [SH-03Cのカーネルソース、ドライバソース](https://sh-dev.sharp.co.jp/android/modules/oss/index.php?/sh03c)
* [古めのAndroid NDK](http://dl.google.com/android/ndk/android-ndk-r8e-linux-x86_64.tar.bz2)
  * 新しいとビルドできなかったりする
* adb
  * androidへの接続に利用
  * Android SDKに入ってるものか、その辺に転がっているものでもいい
* busybox
  * いろいろ便利なので
  * SHBreakを使う場合は自動で入るので自分で入れなくていい
* unubinize
  * 現行起動イメージのunubinizeに利用
* split_bootimg.pl
  * 現行起動イメージの分解に利用
* mkbootfs
  * 起動イメージの作成に利用
* ubinize
  * 起動イメージのubinizeに利用
* flash_image
  * 起動イメージの書き込みに利用
* Wifi接続
  * apt-getとかするので無いとあとでつらい
* 泣くほど時間かかる状況に耐える精神
  * 他の作業の合間にコマンド打つくらいがストレスがたまらなくて良い

## 注意事項

* この機種では（というか今回のメーカーの多くの端末では）**電源OFF状態から通常起動以外のモードで起動する手段が存在しない**ため、boot領域にカーネルイメージを書き込んで起動できなくなると廃棄するしかなくなる。従って、試す際はカーネルイメージは必ずrecovery領域に書き込み、adb reboot recoveryなどでRecovery Modeから起動する。

* また、起動できないイメージをrecovery領域に書き込んである状態でFactory Resetすると再起動ループになり、boot領域に救済可能なカーネルが書き込まれていない限り破棄するしかなくなるため、これも注意する。

* 最も安全なのは、boot領域にadb可能かつinitの進行をサスペンドできるカーネルが書き込んである状態。この状態であれば、大体どんなひどい状況になっても復旧可能。
  * こうなっていると、極端な話、/system下をtgzか何かにしてとってあれば、/systemをerase_imageしても復旧可能。

## 下準備

### 本体の入手

まずSH-03C本体を入手する。動くだけでよければ、中古でおよそ1,000円前後で手に入る。

### ネットワーク接続

androidの設定画面からWiFi接続を行い、インターネットに出られるようにしておく。

### 権限昇格

rootになっておく必要がある。この端末は/dev/diagの脆弱性などで権限昇格が可能。
脆弱性がはっきりしているため、自分でカーネルメモリを書き換えてもいいし、
手に入るようであれば庵怒露慰怒さんのSHBreakでもいいし、そうでなければ[fi01さんのandroid_run_root_shell](https://github.com/fi01/android_run_root_shell)なんかがお勧め。

### recoveryのwrite protect解除

recoveryに自分で作ったカーネルを入れたいので、write protectを解除する必要がある。
この端末は/dev/shsdの脆弱性や、/dev/ae2の脆弱性を利用してrecoveryのwrite protectを解除できる。
これも脆弱性がはっきりしているので、自分でやってもいいし、
手に入るようであれば、[ただぐだぐだと(新)さん](http://ftake.gensoukyo.net/blog/2011/12/14/442)（元のIS01版は[goroh_kunさん](https://twitter.com/goroh_kun)作）の[SH-03C向けae2break](https://www.dropbox.com/s/y545hubde161j27/ae2brek_for_sh03c.zip)が楽でいい。

```bash:recoveryのwrite_protect解除
$ adb push busybox /data/local/tmp
$ adb push ae2break /data/local/tmp
$ adb shell
### 以下、androidで操作
$ su
# chmod 755 /data/local/tmp/busybox
# chmod 755 /data/local/tmp/ae2break
# stop mediayamaha
# kill -9 `/data/local/tmp/busybox pidof mediayamahaserver`
# /data/local/tmp/ae2break recovery
```

stop mediayamahaで端末が再起動することがあるが、気にせず何回か試しているとそのうち成功する。
とりあえず、ここまででrecovery領域にイメージを書き込めるようになった。
このあとは書き込むイメージを作成する。

## カーネルソースのダウンロード

[ここ](https://sh-dev.sharp.co.jp/android/modules/oss/index.php?/sh03c)から以下の２つをダウンロードして展開する。

* kernel.tar.gz
* wifi_kernel_driver_build212.tar.gz

```bash:展開と設置
$ tar xzf kernel.tar.gz
$ tar xzf wifi_kernel_driver_build212.tar.gz
$ mkdir -p external/synergy/modules/wifi/kernel_driver
$ mv build212 external/synergy/modules/wifi/kernel_driver/
```

## カーネルのビルド

### 動作中の本体から現行カーネルのconfigを取得

動作中の端末の/procから取得したconfig.gzを展開し、configを.configにリネームしてカーネルソースのディレクトリに配置する。

```bash:現行configの取得
$ adb pull /proc/config.gz
$ gzip -d config.gz
$ mv config kernel/.config
```

### カーネルの設定

あとづけでOVSを動作させるためには、linux bridgeingをModule Installにしておく必要があるため、以下を.configに設定する。

```properties:kernel/.config
CONFIG_BRIDGE=m
```

menuconfigで設定すると楽でいい。ついでに、netであればvethやgre、filesystemであればnfsやcifsなんかを有効にしておくと後々便利なことがあるかも。

### ソース修正

一部ビルドできないものがある。
以下の２ソースを修正しておく。

```c:kernel/arch/arm/mach-msm/sh_sleepcheck.c
//#include <smd_private.h>
// 以下に修正
#include "smd_private.h"
```

```c:kernel/kernel/cpuset.c
/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */
static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cont,
                 struct task_struct *tsk, bool threadgroup)
{
    int ret;
    struct cpuset *cs = cgroup_cs(cont);

    // "tsk"と"task"で変数名が異なっているので修正
    // if ((current != task) && (!capable(CAP_SYS_ADMIN))) {
    if ((current != tsk) && (!capable(CAP_SYS_ADMIN))) {
        const struct cred *cred = current_cred(), *tcred;

        if (cred->euid != tcred->uid && cred->euid != tcred->suid)
            return -EPERM;
    }
```

### ビルド

上記を直したらmakeする。

```bash:make
$ cd kernel
$ export CROSS_COMPILE=arm-linux-androideabi-
$ export ARCH=arm
$ export PATH=$PATH:/opt/android-ndk-r8e/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86_64/bin
$ make
  CHK     include/linux/version.h
…
  LD [M]  net/bridge/bridge.ko
  CC      net/llc/llc.mod.o
  LD [M]  net/llc/llc.ko
```

ビルドは少し時間がかかるが、放っておけばそのうち終わる。kernel/arch/arm/boot/zImageができる。

### イメージの作成

出来上がったzImageと、現行のboot領域のinitrdを使って起動イメージを作る。

まず現行のinitrdがほしいので、取得する。

```bash:boot領域のダンプ
$ adb shell
### 以下、androidで操作
$ su
# dd if=/dev/mtd/mtd0 of=/data/local/tmp/boot_org.img.ubi
```

イメージはubinizeされているので、unubinizeする。

```bash:unubinize
$ adb pull /data/local/tmp/boot_org.img.ubi
$ unubinize.pl boot_org.img.ubi
```

実行するとboot_org.img.ubi.outができるので、これをカーネルとinitrdに分解する。

```bash:現行起動イメージの分解
$ split_bootimg.py boot_org.img.ubi.out
Page size:      2048 (0x00000800)
Kernel addr:    268468224 (0x10008000)
Kernel size:    6660496 (0x0065a190)
Ramdisk addr:   285212672 (0x11000000)
Ramdisk size:   3177984 (0x00307e00)
Second size:    0 (0x00000000)
Second addr:    284164096 (0x10f00000)
Board name:     
Command line: console=ttyDCC0 androidboot.hardware=qcom androidboot.console=tty0
Base Address: 0x10000000
Writing boot_org.img.ubi.out-kernel ... complete.
Writing boot_org.img.ubi.out-ramdisk.cpio ... complete.
```

分解したら、新しいzImageと分解して出来たinitrdで起動イメージを作成する

```bash:起動イメージの作成
$ mkbootimg --kernel kernel/arch/arm/boot/zImage --ramdisk boot_org.img.ubi.out-ramdisk.cpio --cmdline 'console=ttyDCC0 androidboot.hardware=qcom androidboot.console=tty0' --base 0x10000000 -o boot_new.img
```

できあがったイメージはubinizeして初めて利用可能になるため、ubinizeする。
ubi.cfgをまず編集する。

```ini:ubi.cfg
[boot]
mode=ubi
image=boot_new.img
vol_id=0
vol_size=200MiB
vol_type=dynamic
vol_name=boot
vol_flags=autoresize
```

編集が終わったら、ubinizeを実行する。

```bash:ubinize
$ ubinize -o "boot_new.img.ubi" -p 128KiB -m 2048 -O 256 "ubi.cfg"
```

ここまでで、端末に書き込む用の起動イメージであるboot_new.img.ubiができる。

### 端末のカーネルを更新

作った起動イメージをrecovery領域に書き込む。
端末は前の手順で行ったとおり、recoveryのwrite protectが解除されているとする。

```bash:端末のカーネルを更新
$ adb push flash_image /data/local/tmp
$ adb push boot_new.img.ubi /data/local/tmp
$ adb shell
### 以下、androidで操作
$ su
### recoveryのwrite protectを解除してなければ、ここで解除
# [ae2breakなどで適当に]
### recovery領域に書き込み
# cd /data/local/tmp
# chmod 755 ./flash_image
# ./flash_image recovery ./boot_new.img.ubi
### 終わったら再起動
# reboot recovery
```

これで再起動した後は、さっき設定をいじったとおりのカーネルで起動しているはず。unameで見てみる。

```bash:カーネルの確認
$ adb shell
$ uname -a
Linux localhost 2.6.32.9-perf #7 PREEMPT Fri Sep 11 23:20:35 JST 2015 armv7l GNU/Linux
```

## arm版ubuntuの準備

このままだとovsやrubyのインストールがつらいので、カーネルだけandroidのままで、その上でubuntuを使うようにする。
syscall関係で動かないような場合、カーネルソースとツールについて修正して自分でビルドする（iproute2のsetnsなどがいい例）。

[このあたり](http://cdimage.ubuntu.com/releases/12.04.5/release/ubuntu-12.04-preinstalled-desktop-armhf+ac100.tar.gz)のをダウンロードし、展開してSDCardに入れておく。
恐らくイメージサイズが足りなくなるので、イメージを大きく作りなおして中身をコピーしておいたほうが無難。

### chroot

arm版ubuntuのイメージをSDCard直下にubuntu.img（ext2）として入れたとして、これをrootfsにする。

```bash:chroot
$ adb shell
$ mkdir -p /data/local/tmp/ubuntu
### 以下、androidで操作
$ su
# /data/local/tmp/busybox losetup /dev/block/loop0 /mnt/sdcard/ubuntu.img
# /data/local/tmp/busybox mount -t ext2 /dev/block/loop0 /data/local/tmp/ubuntu
# /data/local/tmp/busybox mount -o bind /dev /data/local/tmp/ubuntu/dev
# /data/local/tmp/busybox mount -o bind /dev/pts /data/local/tmp/ubuntu/dev/pts
# /data/local/tmp/busybox mount -t sysfs /sys /data/local/tmp/ubuntu/sys
# /data/local/tmp/busybox mount -t proc /proc /data/local/tmp/ubuntu/proc
# /data/local/tmp/busybox mount
…
/dev/block/loop0 on /data/local/tmp/ubuntu type ext2 (rw,relatime,errors=continue)
tmpfs on /data/local/tmp/ubuntu/dev type tmpfs (rw,relatime,mode=755)
devpts on /data/local/tmp/ubuntu/dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
/sys on /data/local/tmp/ubuntu/sys type sysfs (rw,relatime)
/proc on /data/local/tmp/ubuntu/proc type proc (rw,relatime)
# export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
# export TERM=xterm
# export HOME=/root
# chroot /data/local/tmp/ubuntu /bin/bash
### 以下、ubuntu
root@localhost:/# cd
root@localhost:~# 
```

ubuntuに入れ替わったら、一回リポジトリをアップデートしておく。

```bash:リポジトリをアップデート
root@localhost:~# apt-get update
…
Fetched 3588 kB in 29s (123 kB/s)                                              
Reading package lists... Done
root@localhost:~#
```

### ovsのインストール

ここまでくると大抵のものが非常に遅いながらも動作するので、例えばaptで開発ツール類を揃えるのは簡単。

ovsはソースからビルドして利用してみる。
この際、現行のカーネルのあれこれが必要になるので、/lib/modules/\`uname -r\`にあらかじめ置いておく（version.hが必要なので、一度ビルドされているものであること）。

```bash:現行カーネル
root@localhost:~# uname -r
2.6.32.9-perf
root@localhost:~# ls -la /lib/modules/`uname -r`
total 85740
drwxrwxrwx  4 root root     4096 Sep 11 23:59 .
drwxr-xr-x  4 root root     4096 Sep 11 23:42 ..
drwxr-xr-x 25 1000 1000     4096 Sep 11 23:59 build
drwxrwxrwx  2 root root     4096 Sep 11 23:59 extra
-rw-rw-rw-  1 root root 87623903 Sep 11 23:59 kernel.tgz
-rw-rw-rw-  1 root root      200 Sep 11 23:59 modules.alias
-rw-rw-rw-  1 root root      159 Sep 11 23:59 modules.alias.bin
-rw-rw-rw-  1 root root       69 Sep 11 23:59 modules.ccwmap
-rw-rw-rw-  1 root root      212 Sep 11 23:59 modules.dep
-rw-rw-rw-  1 root root      466 Sep 11 23:59 modules.dep.bin
-rw-rw-rw-  1 root root       52 Sep 11 23:59 modules.devname
-rw-rw-rw-  1 root root       73 Sep 11 23:59 modules.ieee1394map
-rw-rw-rw-  1 root root      141 Sep 11 23:59 modules.inputmap
-rw-rw-rw-  1 root root       81 Sep 11 23:59 modules.isapnpmap
-rw-rw-rw-  1 root root       74 Sep 11 23:59 modules.ofmap
-rw-rw-rw-  1 root root       99 Sep 11 23:59 modules.pcimap
-rw-rw-rw-  1 root root       43 Sep 11 23:59 modules.seriomap
-rw-rw-rw-  1 root root      131 Sep 11 23:59 modules.softdep
-rw-rw-rw-  1 root root     2936 Sep 11 23:59 modules.symbols
-rw-rw-rw-  1 root root     3503 Sep 11 23:59 modules.symbols.bin
-rw-rw-rw-  1 root root      189 Sep 11 23:59 modules.usbmap
root@localhost:~# 
```

そこまでが完了している前提で、ovsをインストールする。
スマートフォンの日時を直しておかないとconfigureが終わらなくなるので、直しておくこと。

```bash:時刻修正
root@localhost:~# date -s "2015/09/12 00:00:00"
Sat Sep 12 00:00:00 UTC 2015
```

```bash:ovsビルド準備
root@localhost:~# apt-get install -y git automake autoconf gcc uml-utilities libtool build-essential git pkg-config
root@localhost:~# wget http://openvswitch.org/releases/openvswitch-2.3.1.tar.gz
root@localhost:~# tar zxf openvswitch-2.3.1.tar.gz
root@localhost:~# cd openvswitch-2.3.1
root@localhost:~/openvswitch-2.3.1# 
```
以下のソースでNUMA_NODE_NOが未定義なので、-1にしておく。

```c:datapath/flow.c
    //if (likely(flow->stats_last_writer != NUMA_NO_NODE)
    if (likely(flow->stats_last_writer != -1)
        && likely(!rcu_dereference(flow->stats[node]))) {
```

```c:datapath/flow_table.c
    //flow->stats_last_writer = NUMA_NO_NODE;
    flow->stats_last_writer = -1;
```

これが終わったらビルド。

```bash:ovsのビルド
root@localhost:~/openvswitch-2.3.1# ./boot.sh
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, `build-aux'.
libtoolize: copying file `build-aux/config.guess'
…
libtoolize: copying file `m4/lt~obsolete.m4'
root@localhost:~/openvswitch-2.3.1# ./configure --with-linux=/lib/modules/`uname -r`/build
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
…
config.status: executing include/openflow/openflow.h.stamp commands
config.status: executing utilities/bugtool/dummy commands
root@localhost:~/openvswitch-2.3.1# make
make  all-recursive
make[1]: Entering directory `/root/openvswitch-2.3.1'
…
touch include/openflow/openflow.hstamp
make[2]: Leaving directory `/root/openvswitch-2.3.1'
make[1]: Leaving directory `/root/openvswitch-2.3.1'
root@localhost:~/openvswitch-2.3.1# 
```

結構時間がかかるが、耐えるというか、放置する。
完了したら、ovsの起動を確認してみる。

```bash:ovsの起動確認
root@localhost:~/openvswitch-2.3.1# insmod datapath/linux/openvswitch.ko 
root@localhost:~/openvswitch-2.3.1# lsmod
Module                  Size  Used by
openvswitch            78130  0 
root@localhost:~/openvswitch-2.3.1# 
root@localhost:~/openvswitch-2.3.1# mkdir -p /usr/local/etc/openvswitch
root@localhost:~/openvswitch-2.3.1# ./ovsdb/ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
root@localhost:~/openvswitch-2.3.1# ./ovsdb/ovsdb-server -v --remote=punix:/usr/local/var/run/openvswitch/db.sock \
  --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
  --private-key=db:Open_vSwitch,SSL,private_key \
  --certificate=db:Open_vSwitch,SSL,certificate \
  --pidfile --detach --log-file
2015-09-12T00:37:05Z|00001|vlog|INFO|opened log file /usr/local/var/log/openvswitch/ovsdb-server.log
2015-09-12T00:37:06Z|00002|daemon_unix|DBG|/usr/local/var/run/openvswitch/ovsdb-server.pid: deleted stale pidfile
2015-09-12T00:37:06Z|00003|hmap|DBG|lib/shash.c:112: 6 nodes in bucket (16 nodes, 8 buckets)
root@localhost:~/openvswitch-2.3.1# ./utilities/ovs-vsctl --no-wait init
root@localhost:~/openvswitch-2.3.1# ./vswitchd/ovs-vswitchd --pidfile --detach
2015-09-12T00:38:32Z|00001|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connecting...
2015-09-12T00:38:32Z|00002|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connected
root@localhost:~/openvswitch-2.3.1# ./utilities/ovs-vsctl add-br br0
root@localhost:~/openvswitch-2.3.1# ./utilities/ovs-vsctl show      
fd1f82c9-8f57-440a-ba5f-a778833896de
    Bridge "br0"
        Port "br0"
            Interface "br0"
                type: internal
root@localhost:~/openvswitch-2.3.1# ./utilities/ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=13.746s, table=0, n_packets=0, n_bytes=0, idle_age=13, priority=0 actions=NORMAL
```

ovsの動作が確認できた。この時点で、他の端末をOpenFlow Controllerにしてこの端末のネットワークを操作することもできる。
今回は、将来的に分散Edge Overlayも見据えて、Controllerもこの端末に直接いれてみる。

### rubyのインストール

rubyは普通にソースからインストールしてもいいし、rbenvでインストールしてもいい。
今回はrbenvを使うことにした。

```bash:rbenvでrubyをインストール
root@localhost:~/openvswitch-2.3.1# cd
root@localhost:~# git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
root@localhost:~# echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
root@localhost:~# echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
root@localhost:~# . ~/.bash_profile
root@localhost:~# rbenv --version
rbenv 0.4.0-153-g3b6faa8
root@localhost:~# git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
root@localhost:~# rbenv install --list
Available versions:
…
  2.2.3
…
root@localhost:~# rbenv install -v 2.2.3     
Downloading ruby-2.2.3.tar.gz...
/tmp/ruby-build.20150912004449.10219 ~
HTTP/1.1 200 OK
…
Installed ruby-2.2.3 to /root/.rbenv/versions/2.2.3

/tmp/ruby-build.20150912004449.10219 ~
~
root@localhost:~# 
```

これも割と時間がかかるが、放置しているとそのうち終わる。
インストールが終わったら、動作を確認する。

```bash:rubyの動作確認
root@localhost:~# rbenv rehash
root@localhost:~# rbenv global 2.2.3
root@localhost:~# ruby -v
ruby 2.2.3p173 (2015-08-18 revision 51636) [armv7l-linux-eabihf]
root@localhost:~# 
```

動作するのが確認できた。
bundlerが要るので、入れておく。

```bash:bundlerのインストール
root@localhost:~/# gem install bundler
Fetching: bundler-1.10.6.gem (100%)
/root/.rbenv/rbenv.d/exec/gem-rehash/rubygems_plugin.rb:6: warning: Insecure world writable dir /root/.rbenv/versions in PATH, mode 040777
Successfully installed bundler-1.10.6
Parsing documentation for bundler-1.10.6
Installing ri documentation for bundler-1.10.6
Done installing documentation for bundler after 70 seconds
1 gem installed
root@localhost:~/# 
```

### tremaのインストール

alignmentなどの関係でarmへの移植がとてもつらいため、最新のpure ruby版を利用する。
処理速度が1/5程度に落ちると言われているが、移植性やソースの追いやすさ、debugのしやすさはこちらのほうが遥かに高く、今後armサーバなどにSDNが普及していく（と勝手に思っている）事を考えると、とても助かる。

※ruby+c版は、少し深く入るとCのソースに当たるため、debugが手間だった。

```bash:tremaのインストール
root@localhost:~# git clone https://github.com/trema/trema.git
root@localhost:~# cd trema
root@localhost:~/trema# git checkout -b 0.6.0 refs/tags/0.6.0
Switched to a new branch '0.6.0'
root@localhost:~/trema# rm .ruby-version 
root@localhost:~/trema# bundle install --path=vendor/bundle
/root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/vendor/thor/lib/thor/shell/basic.rb:356: warning: Insecure world writable dir /root/.rbenv/versions in PATH, mode 040777
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
Fetching gem metadata from https://rubygems.org/.........
Fetching version metadata from https://rubygems.org/..
Resolving dependencies.........................................................................................
Using rake 10.4.2
Installing abstract_type 0.0.7
Installing ice_nine 0.11.1
…
root@localhost:~/trema# 
```

### tremaの動作確認

インストールが終わったら、起動を確認する。とりあえずOpenFlow1.0にする。

```bash:tremaに繋ぐbridgeの起動と、HelloTremaの確認
root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl add-br br1 -- set bridge br1  protocols=OpenFlow10 -- set bridge br1 other_config:disable-in-band=true -- set bridge br1 other-config:datapath-id=0000000000000001 -- set bridge br1 other-config:hwaddr=00:00:00:00:00:01 -- set-fail-mode br1 standalone -- set-controller br1 tcp:127.0.0.1:6653
root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl show
fd1f82c9-8f57-440a-ba5f-a778833896de
    Bridge "br0"
        Port "br0"
            Interface "br0"
                type: internal
    Bridge "br1"
        Controller "tcp:127.0.0.1:6653"
        fail_mode: standalone
root@localhost:~/trema# git clone https://github.com/trema/hello_trema.git
root@localhost:~/trema# bundle exec bin/trema run hello_trema/lib/hello_trema.rb
/root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/shared_helpers.rb:78: warning: Insecure world writable dir /root/.rbenv/versions in PATH, mode 040777
/root/trema/trema.gemspec:20: warning: Insecure world writable dir /root/trema/vendor/bundle/ruby/2.2.0/bin in PATH, mode 040777
Trema started.
Hello 0x1!
```

HelloTremaが起動し、switch readyが動作するところまで確認できた。

### 念の為、flowmodとか確認

とりあえずvethをovsに挿してみて、適当にpacket_inやflow_modなどを動かしてみる。

```bash:
root@localhost:~/trema# ip link add v11 type veth peer name v12
root@localhost:~/trema# ip link set v11 up
root@localhost:~/trema# ip link set v12 up
root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl add-port br1 v11
root@localhost:~/trema# ip addr add 10.0.0.1 dev v12
root@localhost:~/trema# route add -net 10.0.0.0/8 dev v12
root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl show    
fd1f82c9-8f57-440a-ba5f-a778833896de
    Bridge "br0"
        Port "br0"
            Interface "br0"
                type: internal
    Bridge "br1"
        Controller "tcp:127.0.0.1:6653"
        fail_mode: standalone
        Port "br1"
            Interface "br1"
                type: internal
        Port "v11"
            Interface "v11"
root@localhost:~/trema# route -n      
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.2.0     0.0.0.0         255.255.255.0   U     0      0        0 wlan0
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 wlan0
10.0.0.0        0.0.0.0         255.0.0.0       U     0      0        0 v12
0.0.0.0         192.168.2.1     0.0.0.0         UG    0      0        0 wlan0
root@localhost:~/trema# 
```

まずvethの片方であるv11をbr1に設定してrouteを設定したので、以下の意味のないControllerをtremaで動かしてみる。成功すればFLOODのflowが入るはず。（tremadayのときは動かなかった）

```rb:サンプル
class TestController < Trema::Controller
  def switch_ready(datapath_id)
    logger.info "switch_ready: #{datapath_id.to_hex}"
  end

  def packet_in(datapath_id, message)
    logger.info "packet_in: #{datapath_id.to_hex}"
    send_flow_mod_add(
      datapath_id,
      {
        match: Match.new(in_port: 1, dl_type: 0x0800),
        actions: SendOutPort.new(:flood)
      }
    )
    logger.info "sent: #{datapath_id.to_hex} flow_mod"
    send_packet_out(
      datapath_id,
      packet_in: message,
      actions: SendOutPort.new(:flood)
    )
  end
end
```

```bash:実行
root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl dump-flows br1
NXST_FLOW reply (xid=0x4):
root@localhost:~/trema# bundle exec bin/trema run test.rb &
[1] 10416
root@localhost:~/trema# /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/bundler-1.10.6/lib/bundler/shared_helpers.rb:78: warning: Insecure world writable dir /root/.rbenv/versions in PATH, mode 040777
/root/trema/trema.gemspec:20: warning: Insecure world writable dir /root/trema/vendor/bundle/ruby/2.2.0/bin in PATH, mode 040777
switch_ready: 0x1

root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl dump-flows br1
NXST_FLOW reply (xid=0x4):
root@localhost:~/trema# packet_in: 0x1
sent: 0x1 flow_mod

root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl dump-flows br1
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=3.198s, table=0, n_packets=0, n_bytes=0, idle_age=3, priority=0,in_port=1 actions=FLOOD
root@localhost:~/trema# 
root@localhost:~/trema# ping 10.0.0.254
PING 10.0.0.254 (10.0.0.254) 56(84) bytes of data.
^C
--- 10.0.0.254 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1008ms

root@localhost:~/trema# /root/openvswitch-2.3.1/utilities/ovs-vsctl dump-flows br1
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=11.068s, table=0, n_packets=3, n_bytes=126, idle_age=1, priority=0,in_port=1 actions=FLOOD
root@localhost:~/trema# 
```

動作するのが確認できた。これでこの端末でOpenFlowを使ったControl Plane、Data Planeを扱う事が可能になった。

# この後

## namespace

ここまでくると端末を並べてコンテナを動かして**ポータブルデータセンター**にしたくなってくるというか、それが元々の目的なので、その前にcgroupや、各種namespace等を有効にする。

ところで、このカーネルはnetnsのオプションを有効にしたところで、netns関連のソースが入っていないので機能しない。仕方がないので、Kernel2.6系でnetnsといえばCentOS6.Xということで、パッチをもってきて無理矢理当てる。

幸い、[パッチを公開してくれている方](https://github.com/unicell/redpatch/commits/rhel-2.6.32-358.6.2.ns.el6)がいらっしゃるので、これを利用させて頂く。

この状態ではまだ不十分で、netns系の操作を行うとFunction not implementedと言われる。
そもそもarch/arm/include/calls.Sを見ると、setnsが定義されていない。つまり、setnsを利用したプログラムは動作しない。そのため、自分でcalls.Sに適当にsetnsを追加する。また、iproute等のsyscall番号を直してビルドすることで、一応利用可能にはなる。

## vethのsetns

早速vethに対してsetnsを実行しようかとおもったところ、invalid argumentと言われてしまう。
bridgeのようにsetns localが設定されている場合によく発生する現象だが、ethtoolで見てもvethはそうなっていない。
これについては調査中。