送信パケットと受信パケットでゲートウェイを分ける

# はじめに
こんにちは、今回は送信パケットと受信パケットでゲートウェイを分ける方法を解説します。具体的には、パケットマーキングとポリシーベースルーティング（PBR）を使って、条件を満たすACKパケットだけが別のルーティングテーブルに従うようなルーティングを実装します。

# 想定環境
PCとルーターがローカルネットワーク（`192.168.0.0/24`）で繋がっています。ルーターはローカルネットワークの他に、二つの外部ネットワーク（`X.X.X.0/24`、`Y.Y.Y.0/24`）に属しています。各ネットワークのゲートウェイは`192.168.0.1`、`X.X.X.1`、`Y.Y.Y.1`です。

今回解説するのは、ローカルネットワークに属するPCが外部から接続を待ち受けている状況において、PCから確立するセッションと外部から確立するセッションでゲートウェイを分ける方法です。

![img1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/7fbf5bf1-17bf-4325-8483-e2f55c1065b1.png)


# ルーティングテーブル

デフォルトゲートウェイを`X.X.X.1`、外部ネットワークに接続しているNIC名を`ethX`・`ethY`、外部ネットワークにおけるDHCPで割り当てられたIPアドレスを`X.X.X.2`・`Y.Y.Y.2`とすると、ルーターのルーティングテーブルは下記のようになります。
```shell
$ ip route show
defult via X.X.X.1 dev ethX #経路１
X.X.X.0/24 dev ethX proto kernel scope link src X.X.X.2 #経路２
Y.Y.Y.0/24 dev ethY proto kernel scope link src Y.Y.Y.2 #経路３
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1 #経路４
```

このルーティングテーブルは、４つの経路を定義しています。

+ 経路１：全てのパケットは`ethX`を経由して`X.X.X.1`へ送信する
+ 経路２：`X.X.X.0/24`（に含まれるIPアドレス）宛のパケットは`ethX`を経由して送信する
+ 経路３：`Y.Y.Y.0/24`（に含まれるIPアドレス）宛のパケットは`ethY`を経由して送信する
+ 経路４：`192.168.0.0/24`（に含まれるIPアドレス）宛のパケットは`eth0`を経由して送信する

経路１はデフォルトゲートウェイの定義、経路２・経路３・経路４は隣接ネットワーク（ルーターが属しているネットワーク）の定義です。宛先IPアドレスの条件がより厳しい経路が優先的に選択されるため、適用順序は「経路２→経路３→経路４→経路１」となります。つまり、このルーティングテーブルが意味するところは、

+ 隣接ネットワークに含まれるIPアドレス宛のパケットは、そのIPアドレスへ直接送信する
+ それ以外のパケットは、`X.X.X.1`へ送信する

これだけです。

# 問題点
前節で示したルーティングテーブルの問題点は、PCが`Y.Y.Y.0/24`以外のIPアドレスからSYNパケットを受信した時に、セッションの確立に失敗することです。

例として、外部のホスト（`Z.Z.Z.2`）がSYNパケットを送信した場合を考えます。
SYNパケットを受信したルーターは、ポートフォワードにより宛先IPアドレスをPCのプライベートアドレスへ変換してPCへ送信します。SYNパケットを受信したPCは、`Z.Z.Z.2`宛のACKパケットを作成し、デフォルトゲートウェイであるルーターへ送信します。ACKパケットを受信したルーターは、宛先`Z.Z.Z.2`を見て経路１を選択し`X.X.X.1`へ送信します。その結果、外部ホストは`Y.Y.Y.1`へ送ったSYNパケットのACKを`X.X.X.2`から受信したことで、不整合をおこします。

![img2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/26b9e913-b9cc-4256-840b-c98e3099c272.png)


# 解決策
PCが`Z.Z.Z.2`へ送信したACKパケットが`Y.Y.Y.2`から送信されるように、ルーティングテーブルの追加・パケットマーキング・ポリシーベースルーティング（PBR）を実施します。

具体的には、下記の通りです。
1. `Y.Y.Y.1`をデフォルトゲートウェイにもつ専用のルーティングテーブルを追加する
1. `Y.Y.Y.2`へ届いたSYNパケットに関連するパケットをマークするルールを書く
1. マークされたパケットが追加したテーブルを選択するようにポリシーを書く

### 手順１：ルーティングテーブルの追加
`Y.Y.Y.1`をデフォルトゲートウェイにもつ専用のルーティングテーブルを作成します。
`/etc/iproute2/rt_tables`を編集し、ルーティングテーブルを追加します。
```sh:/etc/iproute2/rt_tables
#
# reserved values
#
255	local
254	main
253	default
100 tableY #追加
#
# local
#
```
:::note
Linuxではルーティングテーブルを複数定義することができます。デフォルトでは３つのテーブル（`local`, `main`, `defult`）が定義されており、それぞれ下記の役割を担っています。
+ `local`：自分自身のローカルアドレスや直接接続されたアドレスに関する経路が含まれるテーブルで、ユーザーが直接操作することはない
+ `main`：通常のルーティングエントリーが含まれる標準的なテーブルで、ipコマンドのtableオプションを省略した場合に選択される
+ `defult`：デフォルトのルート情報が含まれるテーブルで、全ての通信が行き先を明確にできない場合に使用される

パケットがどのルーティングテーブルに従うかは、ルーティングポリシーにより決定されます。
:::

追加したルーティングテーブルに経路を設定します。経路５はPCが送信したACKパケットを外部ホスト（`Z.Z.Z.2`）へ送信するための経路で、経路６は`Y.Y.Y.2`へ届きポートフォワードされたSYNパケットをPCへ送信するための経路です。

```shell
$ ip route show table tableY
defult via Y.Y.Y.1 dev ethY #経路５
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1 #経路６
```

このテーブルを選択するためのポリシーは後で書きます。

### 手順２：パケットマーキング
ACKパケットを識別できるように、`iptables`を用いてマーキングします。ただし、PCはローカルネットワーク（`192.168.0.0/24`）にある機器からもSYNパケットを受信する可能性があるため、全てのACKパケットをマーキングする訳にはいきません。

そこで、`Y.Y.Y.2`へ届いたSYNパケットをマーキングした後、`conntrack`で関連パケットを追跡します。`conntrack`モジュールを用いると、既にマーキングされたSYNパケットの関連パケットに対して、SYNと同じマークを付けることができます。
```sh
iptables -t mangle -A PREROUTING -i ethY -p tcp --syn -d Y.Y.Y.2 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -i eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j MARK --set-mark 1
```
これにより、`Y.Y.Y.2`へ届いたSYNパケットに紐づくパケットだけがマーキングされた状態になります。

:::note
`iptables`により付与されたマークは、`iptables`を実行しているマシンの内部で管理されているため、他マシンから参照することはできません。
:::

### 手順３：ポリシーベースルーティング（PBR）
マークされたパケットが専用のルーティングテーブルを参照するように、ルーティングポリシーを追加します。プライオリティが低いポリシーが優先して適用されるため、`main`テーブルよりも低い値を設定します。

```sh
$ ip rule add fwmark 1 lookup tableY priority 32765
$ ip rule show
0:      from all lookup local
32765:  from all fwmark 1 lookup tableY
32766:  from all lookup main
32767:  from all lookup default
```

# まとめ
以上で、PCから確立するセッションと外部から確立するセッションでゲートウェイを分けることができました。ハニーポットの構築やマルウェア解析など、変則的なパケット制御が求められる場面でお役に立てれば幸いです。

![img3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007841/85fea063-3cce-4849-9377-f27465b1a6ce.png)