TCP BBR＋VPN＠L2TPでMVNOを早くする on CentOS 7 on Azure

TCP BBR+ShadowsocksRの記事（[Link](https://qiita.com/hotchpotch/items/ada8a2a1297e2e461ac3)）を見かけたので、自分用にVPNサーバーを立てたのでそのメモ。TCP BBRについてはこの辺のQiitaを適当に参照のこと。
https://qiita.com/fallout/items/92b2099ab5e16cfeb1f9

## TL;DR
- TCP BBRを有効にしたVPNサーバを経由することで、MVNO平日昼の低速時間帯でも通信速度を上げられる
- 低速時間帯**以外**ではVPN無しの方が早いこともあったので、切り替え運用が有効そう

---
## 今回の構想
Azure Linux VM上にL2TP-VPN GWを立て、iPhone/Android etc.からはそのGWへVPN on MVNOを張ることで日中の低速地獄をなんとかできないか？という実験

## 構成
#### Azure VM
US-West2にB1sを一台。自分用+所詮はMVNOの帯域なので大した負荷は掛からないだろうという予想。**何よりJPリージョンよりも安い**

#### TCP BBR
自分で入れても良いが、今回の目的では無いので[teddysun氏のAuto installer](https://github.com/teddysun/across/blob/master/bbr.sh)を利用する。

#### VPNサーバ
xl2tp+LibreSwan
> 他でも良い。例えば金盾対応のShadowsocksとか。

## インストール手順
### 1. VMを準備する
特に書くことは無いが、いつも通りAzureでVMを準備する。別にAWSでもGCPでも良いし、自宅にサーバが余っているなら当然それでも良い。今回のOSはCentOS 7.6で話を進める。

### 2. 必要なものを入れる
```console
# yum -y update && yum install -y git epel-release iptables-services && yum -y update
# git clone https://github.com/teddysun/across.git
```
>EPELはTCP BBRには要らないがVPNで使うので入れておく。
>firewalldの方が好みならそれでもOK。

### 3. TCP BBRのインストール
```console
# /bin/bash across/bbr.sh
---------- System Information ----------
 OS      : CentOS 7.6.1810
 Arch    : x86_64 (64 Bit)
 Kernel  : 3.10.0-862.11.6.el7.x86_64
----------------------------------------
 Auto install latest kernel for TCP BBR

 URL: https://teddysun.com/489.html
----------------------------------------

Press any key to start...or Press Ctrl+C to cancel
```
自動的に作業が進む。手作業だとgrub.confやsysctl.confを弄ったりする必要がでるが全部自動。たすかる～

```console:インストールログ
Retrieving https://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
Preparing...                          ########################################
Updating / installing...
elrepo-release-7.0-3.el7.elrepo       ########################################
Failed to set locale, defaulting to C
Failed to set locale, defaulting to C
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * elrepo: elrepo.org
 * elrepo-kernel: elrepo.org
Resolving Dependencies
--> Running transaction check
---> Package kernel-ml.x86_64 0:5.0.9-1.el7.elrepo will be installed
---> Package kernel-ml-devel.x86_64 0:5.0.9-1.el7.elrepo will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch       Version                 Repository         Size
================================================================================
Installing:
 kernel-ml           x86_64     5.0.9-1.el7.elrepo      elrepo-kernel      47 M
 kernel-ml-devel     x86_64     5.0.9-1.el7.elrepo      elrepo-kernel      12 M

Transaction Summary
================================================================================
Install  2 Packages

Total download size: 59 M
Installed size: 262 M
Downloading packages:
--------------------------------------------------------------------------------
Total                                               34 MB/s |  59 MB  00:01
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Warning: RPMDB altered outside of yum.
  Installing : kernel-ml-devel-5.0.9-1.el7.elrepo.x86_64                    1/2
  Installing : kernel-ml-5.0.9-1.el7.elrepo.x86_64                          2/2
  Verifying  : kernel-ml-5.0.9-1.el7.elrepo.x86_64                          1/2
  Verifying  : kernel-ml-devel-5.0.9-1.el7.elrepo.x86_64                    2/2

Installed:
  kernel-ml.x86_64 0:5.0.9-1.el7.elrepo
  kernel-ml-devel.x86_64 0:5.0.9-1.el7.elrepo

Complete!

Info: The system needs to reboot.
Do you want to restart system? [y/n]
```
> どうもCentOS 7.xならELrepoからMainLineさえ持ってくれば済むようなので、手作業でやっても大した手間ではなかった気が後からした

#### (Optional) TCP BBRが有効になったかの確認
```console:bbrとでていればOK
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = bbr
```

### 4. L2TP VPNのインストールと設定
```console
yum install -y xl2tpd libreswan
```
```console:/etc/xl2tpd/xl2tpd.conf
[global]
listen-addr = YOUR_IPADDRESS

[lns default]
ip range = 10.2.2.100-10.2.2.150
local ip = 10.2.2.1
require chap = yes
refuse pap = yes
require authentication = yes
name = yourdomain.example.com
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
```
> YOUR_IPADDRESSにはVMの`eth0アドレス`を設定する。

```console:/etc/ppp/options.xl2tpd
ipcp-accept-local
ipcp-accept-remote
ms-dns  8.8.8.8
noccp
auth
idle 1800
mtu 1200
mru 1200
nodefaultroute
debug
proxyarp
connect-delay 5000
name xl2tpd
refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2
persist
logfile /var/log/xl2tpd.log
```
> `lock`と`crtscts`が入っているとエラーになるので消すかC/Oすること。

```console:/etc/ppp/chap-secrets
# Secrets for authentication using CHAP
# client        server  secret                  IP addresses
yourusername yourdomain.example.com "yourpassword" *
```
```console:/etc/ipsec.d/l2tp-ipsec.conf
conn L2TP-PSK-NAT
        rightsubnet=0.0.0.0/0
        dpddelay=10
        dpdtimeout=20
        dpdaction=clear
        forceencaps=yes
        also=L2TP-PSK-noNAT
conn L2TP-PSK-noNAT
        authby=secret
        pfs=no
        auto=add
        keyingtries=3
        rekey=no
        ikelifetime=8h
        keylife=1h
        type=transport
        left=YOUR_IPADDRESS
        leftprotoport=17/1701
        right=%any
        rightprotoport=17/%any
```
> YOUR_IPADDRESSの扱いは先と同様。

```console:/etc/ipsec.d/default.secrets
: PSK "事前共有鍵"
```
```console
chmod 200 /etc/ipsec.d/default.secrets
```

### 5. kernel paramの設定
```console:/etc/sysctl.d/60-ipsec.conf
net.ipv4.ip_forward=1
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.eth0.accept_redirects=0
net.ipv4.conf.eth0.rp_filter=0
net.ipv4.conf.eth0.send_redirects=0
net.ipv4.conf.lo.accept_redirects=0
net.ipv4.conf.lo.rp_filter=0
net.ipv4.conf.lo.send_redirects=0
```
```console
# sysctl --system
```
> 反映させるために一旦`init 6`

### 6. firewallの設定（for VPN）
```console
# iptables -t nat -A POSTROUTING -s 10.2.2.0/255.255.255.0 -j MASQUERADE
# /usr/libexec/iptables/iptables.init save
# systemctl enable iptables
```
VPNトンネルからWANへのルーティング設定。firewalldの場合は同等の設定を。
>今回は他のREJECTルールは無し。Azure側で弾くため。

#### Azure側の設定
使用するVMに刺さっているNICのNSGで以下をACCEPTする。

- 500/UDP
- 1701/UDP
- 4500/UDP

### 7. 設定確認
```console
# ipsec verify
Verifying installed system and configuration files

Version check and ipsec on-path                         [OK]
Libreswan 3.25 (netkey) on 5.0.9-1.el7.elrepo.x86_64
Checking for IPsec support in kernel                    [OK]
 NETKEY: Testing XFRM related proc values
         ICMP default/send_redirects                    [OK]
         ICMP default/accept_redirects                  [OK]
         XFRM larval drop                               [OK]
Pluto ipsec.conf syntax                                 [OK]
Two or more interfaces found, checking IP forwarding    [OK]
Checking rp_filter                                      [OK]
Checking that pluto is running                          [OK]
 Pluto listening for IKE on udp 500                     [OK]
 Pluto listening for IKE/NAT-T on udp 4500              [OK]
 Pluto ipsec.secret syntax                              [OK]
Checking 'ip' command                                   [OK]
Checking 'iptables' command                             [OK]
Checking 'prelink' command does not interfere with FIPS [OK]
Checking for obsolete ipsec.conf options                [OK]
```

## 効果測定
今回の評価は以下の環境で行っている。測定日は平成最後の4/24である。

| MVNO | 地域 | VM Region | 測定サイト |
|:-:|:-:|:-:|:-:|
| LinksMate | 横浜 | US-West2(Washington) | Google |

VPNの終端端末はiPhone 8であるが、MVNO Simは別端末(Xperia)に挿し、そこからのWiFiテザリングによりiPhoneを接続していることを付記しておく。`WAN -- Xperia --(VPN)-- iPhone`のようなイメージになる。
時間ごとの計測結果を以下に示す。間隔が飛び飛びなのは仕事の合間に思い付いたタイミングで計っただけなので容赦いただきたい。朝と夜がないのは帰ったため。データは、Naive/Naive(tether)がそれぞれXperia/iPhone(VPN無し)での計測結果、TCP BBRがiPhone(VPN有り)の結果である。また今回のグラフには示していないが、**単にVPNを張ったこと**による影響ではないことを明らかにするため、別のVPN(TCP BBR無効/国内)でも計測し、Naive(tehter)と同等～やや落ちる程度であることを確認している。

![下り計測結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300527/de5f86f6-9169-5ad9-0ca2-43682586719d.png "下り計測結果")
![上り計測結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300527/6d32577d-bc3c-647e-29f8-c0e5339933ca.png "上り計測結果")

測定はそれぞれ1passしかしていないのでばたついているが、特筆すべきはいわゆる低速時間帯（12:00～13:00）における下り速度の差であろう。VPN無しがともに5Mbpsを割り込んでいるのに対し、TCP BBR有効なVPNを経由することで10Mbpsと他時間帯と同等の帯域幅を確保できていることが分かる。
一方で、それ以外の時間帯についてはVPNにより速度が低下する現象がみられた。今回は他の計測端末を準備しなかったため、VPNを張ったことによるO/Hの影響なのか、それ以外に何かあるのかの切り分けまではできていない。いずれにしても低速時間帯における速度を確保できることは確認できたので良しとする。

#### そもそもなぜこんなことが起こるのか？
単純な測定しかしてないうえ、そもそも輻輳箇所はキャリアの管理下にあるため、どこまで行っても推測の域を出ないが、「なんか良くなるから使う」では気持ち悪いので簡単に考察してみることにする。

低速時間帯における速度低下は、主に我々サラリーマンの需要が昼休みに急増することで、キャリア側（より正確にはMVNO-キャリア間の割り当て帯域）で通信の輻輳が発生し、トラフィックシェーピングが行われた結果、パケロスによる再送となることで生じている（と考えられる）。つまり、この輻輳を何らかの手法で予測し、パケロスにならない程度のギリギリを攻めたパケット送出パターンを実現することができるのであれば、通信路の効率化に繋げることができるだろう。

ここでBBRが何者なのかを考える。理解した範囲でとてもざっくりとした言葉で言い表すのであれば、**RTTやロス率(到達率)から輻輳状態をモデル化していい感じに調整**してくれる制御手法である。問題になるキャリア側でのトラフィックシェーピングなどは、当然ながらロス率やRTTへと表れてくる。すなわち、現在どういう帯域制御が行われているのかをある種予測していると考えられ、これが低速時間帯に効果が見られた理由であろう。特に今回はVPNサーバ側にTCP BBRを有効とした。このため、VPNサーバから端末への経路（≒下り）のパケットが良い感じに調整されていると推測され、これが下りの結果だけで差が見られている理由であろう。

## まとめ
TCP BBRとVPNの組み合わせで、MVNO利用時の大きな課題である平日昼の低速時間帯の通信速度を改善できることを実証することができた。有効性を確認できたので、これからは使っていきたいと思う。
形の上では、この方式を使っている人間だけが周りより得をすることになるため、帯域制御の公平性という観点では問題があると捉える意見もみた。確かにその意見にも一理はあるかとは思うが、そもそもこんなことはキャリア・MVNO側が最初から考え、施行すべきことであり、むしろパケロスを減らして効率化しているのだから感謝して貰いたいぐらいである、というのが私の見解である。


早くこんな手間を掛けなくても済むインフラが実現されて欲しいものだ・・・



## 参考にしたサイトなど
https://secondlife.hatenablog.jp/entry/2019/04/22/082740
https://qiita.com/hotchpotch/items/ada8a2a1297e2e461ac3
https://qiita.com/fallout/items/92b2099ab5e16cfeb1f9
https://qiita.com/mykysyk@github/items/7b952eed0c71befeca47
https://qiita.com/yume_yu/items/09f57a8923341c5b2316
https://itkq.jp/blog/2017/07/31/bbr/