Ubuntuを使いネットワークエミュレータの作成をする

# はじめに
この記事は、私、大阪国際工科専門職大学 [⧉](https://www.iput.ac.jp/osaka/) 、情報工学科、２年のuroketa(本名は伏せます)が、大学のカリキュラムで、iPresence合同会社 [⧉](https://ipresence.jp/) の企業内で実習に臨まさせて頂いた時に作成したものです。
今回は、展示会場などの不安定なインターネット回線を再現するために、Ubuntuをネットワークエミュレータ代わりにし、ネットワークエミュレータを介してtemi(テレプレゼンスロボット)(今回はRICOH THETAもドッキング)に信号を送るエミュレートを行いました。
![スクリーンショット (235).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114111/0d060afd-607f-46ba-2053-c4a6313ecfe8.png)


## ☆注意！
今回ネットワークエミュレータとして使っているUbuntuのバージョンは、**Ubuntu18.04**です。
また、使用するUbuntuのLANポートが１つの場合、**USB変換端子**が必要になります。
そして、WiFi➁は**ブリッジモード**で起動してください！

## ☆Ubuntuにネットワークエミュレータの機能を設定

#### ➀UbuntuとWindowsPCをSSH接続し、LINUXにログインする。
teratarmなどのソフトを使い、WindowsPCからSSH接続でLINUXと接続、Ubuntuにログインし、コマンドを打ち込むための準備をします。(キーボードなどをUbuntuに接続できる場合はSSH接続せずに直接コマンドを打ち込むことも可能ですが。)
#### ➁Ubuntuにブリッジを作成するためのツールをインストールする
LINUX機をネットワークエミュレータ代わりにするには、Ubuntuをブリッジ化(スイッチングハブのような役割)にする必要があります。そのために、まず、**apt**コマンドで、

    sudo apt install bridge-utils
と入力し、ブリッジを作成するためのツール (**bridge-utils**) をインストールします。
ここで注意してほしいのが、先頭の「**sudo**」というコマンドです。この「**sudo**」は、スーパーユーザー（管理者、rootユーザー）」の権限が必要なコマンドを実行することが出来るコマンドで、今後、先頭に「sudo」と書かれたコマンドが出てきますが、それらは全てスーパーユーザーの権限が必要なコマンドだということを理解しておいてください。

#### ➂IPアドレスを削除、固定IPを追加etc..する処理を書くための場所を作成する
Ubuntuブリッジ化するためには、難しい話になりますが、ネットワークインタフェース(以下LANポート)のIPアドレスを削除し、ブリッジに固定IPを追加した後にブリッジ、LANポートを起動する必要があります。(理由は置いておいて)
**ここで登場！** するのが「**shell script（シェルスクリプト）**」です。作成した**shell script**ファイルを実行することで、shellscriptファイル内に記述された処理が実行されます。

shellscriptファイルを新規作成したい時は、

    sudo vi ~/.ファイル名.sh


と入力して下さい。また作成したファイルを編集したい時は、

    sudo vi ファイル名.sh
と入力すればOKです。(間違えて別ファイルを新規作成しないように！)
そして、編集したshellscriptファイルを保存＆編集画面から抜け出したい時は、

    :wq
と入力すればOKです。また、編集画面は開いたが、編集はしなかった時などは、

    :q!
と入力すれば、保存せずに終了も出来ます。そして、作成したshellscriptファイルは、
 
    sudo ファイル名.sh
で実行できます。(ややこしい！)
 
#### ➃shellscript内の処理を記述
shellscript内には、LANポートのIPアドレスを削除し、ブリッジに固定IPアドレスを設定する処理、更にLANポートとブリッジをを起動し、使用可能状態にする処理を記述します。以下をshellscript内に記述します。

    #shellscript上でbridge-utilsツールを使用可能にする
    BRCTL=/sbin/brctl 

    #shellscript上でIPツールを使用可能にする
    IP=/sbin/ip

    #ブリッジの変数を作成
    BRIDGE=ブリッジ名

    #ブリッジに設定したい固定IPアドレスの変数を作成
    IP_ADD=○○○.○○○.○.○/24
    
    #for文用にLANポート名をに文字列で記述する変数を作成
    NICS="LANポート名➀ LANポート名➁"　

    #ブリッジに固定IPアドレスを設定
    $IP addr add $IP_ADD dev $BRIDGE

    #ループ処理($NICにLANポート名を割り振る)
    for NIC in $NICS;
    do
        $IP addr flush dev $NIC            #LANポートのIPアドレスを削除
        $BRCTL addif $BRIDG $NIC           #ブリッジにLANポートを認識させる
        $IP link set dev $NIC promisc on   #LANポートをプロミスキャスモードで起動
        $IP link set dev $NIC up           #LANポートを起動
    done

    #ブリッジを起動
    $IP link set dev $BRIDG up

**ここで注意！** してほしいのが、「＄」という記号です。変数の前に＄と書くことで、その位置に、変数の中の数字が反映されます。(なので、上のコードで言うと、ブリッジ名がbr0だとすると、$BRIDGEとbr0は同じ意味になります。)

これで、作成したshellscriptを実行することで、➃の題名の下に書いた処理が実行され、いつでも遅延やパケロス、帯域制限をかけることができる状態になりました！



#### ➄bashrcを使い、各種通信制限を加える処理を関数化して保存する場所を作成する。
➄の題名に書いているような遅延、パケロス、帯域制限の処理を (➃内のコードも含めて)1行1行コマンドを入力していってもいいのですが、それだとかなりの時間を使ってしまいます。なので、今回は、各種通信制限(遅延、パケロス、帯域制限)を発生させる処理をそれぞれ関数化し、それらを呼び出して引数を渡すだけで実行できるようにしました。
**ここで登場！** するのが「**bashrc**」です。bashrcはshellscriptと違い、PCが起動すると同時にbashrc内の処理を実行させることが出来ます。(なので起動後には既に作成した関数を呼び出せる状況になっています)

bashrcファイルを新規作成(初回)&編集したい時は、

    sudo vi ~/.bashrc
と入力すればOKです。編集したbashrcファイルを保存&bashrc編集画面から抜け出したい時は、

    :wq
と入力してください。こちらも、保存せずに終了したい時は、shellscriptと同じように、

    :q!
と入力すればOKです。また、編集後、PCを再起動すれば編集した内容が反映されるのですが、毎回毎回編集後に再起動するのはかなりの時間ロス!....... そんな時は:wqでbashrcを保存＆終了した後に、

    bash
と入力しましょう。編集した内容が反映され、編集後のコードが使用可能な状態になります。


#### ➅bashrc内の処理を記述
ここで、遂にbashrc内で遅延、パケロス、帯域制限の機能を関数化して記述する時がやってきました。また、ネットワークエミュレータの機能だけでなく、各通信制限をかけたLANポートの制限状態を解除する処理、それぞれインタフェースの状態を確認できる処理を関数化しておくと便利なので追加しておくことにします。
以下がbashrc内に記述したコードになります。

     #遅延処理
     #$1→遅延を加えたいLANポート名
     #$2→遅延をかけたい時間(s)(1000s＝1秒)

    tc_delay(){
    sudo tc qdisc add dev $1 root netem delay $2
    }
    
     #帯域制限処理
     #$1→帯域制限をかけたいLANポート名(Kb→bはByte！bitではない)
     #$2→データキューのサイズ(Kb)
     #$3→バケツのサイズ(Kb)
     #$4→トークン補充速度(帯域幅)(Kb)

    tc_thpt(){
    sudo tc qdisc add dev $1 root tbf limit $2 buffer $3 rate $4
    }

     #パケロス処理
     #$1→パケロスを発生させたいLANポート名
     #$2→何%パケロスさせるか(%)を

    tc_pkloss(){
    sudo tc qdisc add dev $1 root netem loss $2
    }
    
     #通信制限解除処理
     #$1→制限を解除したいLANポート名

    tc_delete(){
    sudo tc qdisc del dev $1 root
    }
    
     #LANポート名の状態確認処理
     #$1→状態を確認したいLANポート名

    tc_show(){
    sudo ts -s qdisc show dev $1
    }
これで、やっと全てのコードの記述が終わりました。(お疲れ様です！)
これからは、前にも書いた通り関数を呼び出して引数を決めればすぐに実行できるので楽です。
**ここで注意!** 関数を呼び出す前に、必ず作ったshellscriptを実行してください。

**さらに注意！ 通信の方向によって、制限をかけるLANポートは変わります。**
今回の実験の場合だと、temi→PCへの信号に制限をかけたい場合はLANポート➀、PC→temiへの信号に制限をかけたい場合はLANポート➁を引数として与えました。

例えば、LANポート➀に３秒の**遅延**(temi→PCへの通信に３秒の遅延)を加えたい場合は、

    tc_delay LANポート➀ 3000s　
と入力します。その他関数も引数に当てはまる値を与えて呼び出すことで実行できます。

## ☆検証内容
PC→temiはその場回転の信号を送ってからのtemiの反応やtemiに映るPCカメラの映像、temi→PCの場合はPCに映るtemiカメラの映像を観察しました。また、結果を数値的にも確認するため、遅延はpingを使ってPC⇔temi間の発信から返信までの時間、パケロスはpingを使い、PC→temiはそのまま、temi→PCへの通信は、結果を計測するためにWiFi➁に繋いだPC➁を用意し、temi代わりとして損失を計測、帯域制限も同じくWiFi➁と接続したPC➁を用意し、temi代わりとして、iperf3を使用して確認しました。また、それぞれの検証を終えるたびに、通信制限を解除し、次の検証に進みました。
#### 何故temi代わりのPCを用意するのか？
temi側からpingを送る手段がないので、temi→PCの通信の結果を数値で表す手段がないから。

## ☆エミュレート結果
まず、何も通信制限をかけなかった時のPC→temiのping通信は

    192.168.3.75 の ping 統計:
      パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
      最小 = 43ms、最大 = 970ms、平均 = 427ms
となりました。そして、temi代わりのPC➁(WiFi➁に接続)→PCのping通信は、

    192.168.3.41 の ping 統計:
      パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
      最小 = 4ms、最大 = 270ms、平均 = 71ms    
となりました。



### ➀遅延の確認

    tc_deley LANポート名➀ 1000ms
    tc_deley LANポート名➁ 1000ms
と入力し、pingによる測定結果は、
  
     192.168.3.75 の ping 統計:
      パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
      最小 = 2003ms、最大 = 2421ms、平均 = 2138ms
となりました。また、その他にも
・その場回転の指示をtemiに送ってからtemiが回転するまでの時間に遅延が発生
・temiカメラの前で手をグーからパーにしてから約PCに映像が反映されるまでの時間に遅延が発生
といった結果が得られました。

### ➁パケロスの確認

    tc_pkloss LANポート名➀ 50%
    tc_pkloss LANポート名➁ 50%
と入力し、pingによる測定結果は、

    192.168.3.75 の ping 統計:(PC→temiの通信)
      パケット数: 送信 = 4、受信 = 2、損失 = 2 (50% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
      最小 = 3ms、最大 = 457ms、平均 = 230ms

    192.168.3.41 の ping 統計:(tmmi代わりのPC➁→PC)
      パケット数: 送信 = 4、受信 = 2、損失 = 2 (50% の損失)、
    ラウンド トリップの概算時間 (ミリ秒):
      最小 = 8ms、最大 = 8ms、平均 = 8ms
となりました。また、その他にも、
・画質がかなり悪くなる
・コマ送りのような映像になる
・PC⇔temi間の通信で遅延は確認されなかった
といった結果が得られました。

### ➂帯域制限(throughput)の確認

    tc_thpt ポート名➀ 10Mbit 5Mbit 10Mbit 
    tc_thpt ポート名➁ 10Mbit 5Mbit 10Mbit
と入力し、iPerfによる測定結果(PC間)は、

    通常
    [ ID] Interval           Transfer     Bandwidth
    [  5]   0.00-10.09  sec  0.00 Bytes  0.00 bits/sec            sender
    [  5]   0.00-10.09  sec  40.9 MBytes  34.0 Mbits/sec        receiver 

    帯域制限を掛けた結果([5]がPC側から通信、[4]がtemi代わりのPCから通信)
    [ ID] Interval           Transfer     Bandwidth
    [  5]   0.00-10.20  sec  0.00 Bytes  0.00 bits/sec            sender
    [  5]   0.00-10.20  sec  12.1 MBytes  9.94 Mbits/sec        receiver

    [ ID] Interval           Transfer     Bandwidth
    [  4]   0.00-10.01  sec  11.5 MBytes  9.64 Mbits/sec          sender
    [  4]   0.00-10.01  sec  11.4 MBytes  9.52 Mbits/sec        receiver

となりました。また、その他にも、
・PC⇔temi間の通信で遅延が発生
・PCに表示されるtemiカメラの映像、temiお互いの映像の画質が荒くなる
といった結果が得られました。

## さいごに
https://qiita.com/ikedaS-06/private/6013c3f733140b18f052

上の記事は同じ実習仲間が作成した同じ内容の記事です。この記事より簡略化されて書かれているのでこの記事が分かりずらかったら上の記事を参考にしてみてください笑。