LinuxでPBR(Policy Base Routing)


CiscoやYAMAHAのルーターならフィルタなどを利用してポリシーベースルーティングできると思います。
同じようにLinuxでもやりたいと思います。

ポリシーベースルーティングとは、通常のルーティングと違い、ホスト内で完結させるルーティング手法。
特定の動作の時に特定のルートテーブルを使う。といった感じです。(説明難しい。。。)
例えば・・・

  • マルチホーム環境で特定のポートにアクセスする場合は、ゲートウェイを変えたい。
  • マルチホーム環境で、入ってきたI/Fのアクセスは応答する時も同じI/Fで出したい。 などなど。。。

キモはマルチホーム環境である事。

例題)特定のポートにアクセスする場合にゲートウェイを変えたい

どんなシチュエーションが考えられるか?
・通常はA回線を使っているが、httpアクセスの時はB回線を使いたい。

今回の環境は
・ホストに外線が3本刺さっており(192.168.0.2,192.168.1.2,192.168.2.2)、その先にゲートウェイ(192.168.0.1,192.168.1.1,192.168.2.1)がそれぞれあるとする。
・ホストから12.34.56.78のhttpアクセスはゲートウェイAから。12.34.56.78のhttpsアクセスはゲートウェイBから。12.34.56.78のsshアクセスはゲートウェイCから。それぞれアクセスするようにしたい。

こんな事を叶えるために必要なLinuxの機能は
* iproute2
* iptables
になります。

iptablesで特定のポートに向かうパケットに印を付け(iptables)、印の付いているパケットは指定のルーティングテーブルを使う(iproute2)。

1.iptablesに印を付ける

パケットに印を付けるにはiptablesのmangleテーブルを使用する。

# iptables -t mangle -A OUTPUT -p tcp --dport 80 -j MARK --set-mark 1 ←tcp80に向かって出ていくパケットに「1」をマークする
# iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 2 ←tcp443に向かって出ていくパケットに「2」をマークする
# iptables -t mangle -A OUTPUT -p tcp --dport 22 -j MARK --set-mark 3 ←tcp22に向かって出ていくパケットに「3」をマークする

2.iproute2にルールを追加する

印を付けたパケットに対するルールを指定する。

# ip rule add fwmark 1 table 10 ←「1」をマークされているパケットは「table10」を参照する
# ip rule add fwmark 2 table 20 ←「2」をマークされているパケットは「table20」を参照する
# ip rule add fwmark 3 table 30 ←「3」をマークされているパケットは「table30」を参照する

更にゲートウェイ宛のインターフェースが複数あるため、戻りパケットにも指定を入れておく。

# ip rule add from 192.168.0.2 table 10 ←「192.168.0.2」に入ってきたパケットは「table10」を参照する
# ip rule add from 192.168.1.2 table 20 ←「192.168.1.2」に入ってきたパケットは「table20」を参照する
# ip rule add from 192.168.2.2 table 30 ←「192.168.2.2」に入ってきたパケットは「table30」を参照する

3.iproute2にルートを追加する

参照させるルートtableを作成する。

# ip route add default dev eth1 via 192.168.0.1 table 10 ←「table10」のデフォルトゲートウェイはeth1の先の192.168.0.1とする
# ip route add default dev eth2 via 192.168.1.1 table 20 ←「table20」のデフォルトゲートウェイはeth2の先の192.168.1.1とする
# ip route add default dev eth3 via 192.168.2.1 table 30 ←「table30」のデフォルトゲートウェイはeth3の先の192.168.2.1とする

それぞれのtableにローカルリンクを追記しておく方が良いので、適宜対応しましょう。
ローカルリンクの書き方は

# ip route show table main

を参照し、default意外を適用すると良いぞ!

ワンライナーだとこんな感じ

# ip route show table main | grep -Ev ^default | while read ROUTE ; do ip route add table "table名" $ROUTE; done

以上で設定は終わり。実際に検証してみよう。
問題なければ /etc/rc.localか何か起動スクリプトに追加するといいかもしれない。