同じサブネットへのNIC 2枚指し、またはソースルーティングのおはなし
「NIC 2枚刺し」というと「IP masqueradeを使ってLinuxでルーターを作ろう!」的な話を思い出す老害です。こんにちは。
NICを2枚生やしたサーバーから同じサブネットに両方の足をのばす機会があったのですが、じゃっかん躓いたのでそのメモです。
具体的にいうと、eth0の方は問題ないのですが、eth1についているIPアドレスへの疎通ができない、というものでした。
以下、
という体で読んでください。
さて、
別サーバーからpingしつつ、eth1でtcpdumpしてみると、echo requestは届いているのが観測できました。が、echo replyを返していません。
# tcpdump -i eth1 -nl icmp listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes 15:19:07.469577 IP 10.0.0.100 > 10.0.0.11: ICMP echo request, id 55821, seq 551, length 64 15:19:08.469517 IP 10.0.0.100 > 10.0.0.11: ICMP echo request, id 55821, seq 552, length 64 15:19:09.469253 IP 10.0.0.100 > 10.0.0.11: ICMP echo request, id 55821, seq 553, length 64
同じようにeth0でtcpdumpしてみると、入ってきたeth1じゃなくて、eth0からecho replyを出しているのが観測できました。
# tcpdump -i eth0 -nl icmp listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes 15:22:14.481126 IP 10.0.0.11 > 10.0.0.100: ICMP echo reply, id 55821, seq 738, length 64 15:22:15.480946 IP 10.0.0.11 > 10.0.0.100: ICMP echo reply, id 55821, seq 739, length 64 15:22:16.480895 IP 10.0.0.11 > 10.0.0.100: ICMP echo reply, id 55821, seq 740, length 64
なので、10.0.0.11宛のパケットは入ってきたeth1から出ていくようにすればOKそうです。
これをハンドリングするにはソースルーティングの設定をすればよくて、Linuxではiproute2の出番になります。
古来のrouteコマンドですと、宛先に応じてNICのデバイスやnext hop routerの指定を行いますが、Linuxではこのテーブルを複数持つことができ、さまざまな条件でどのテーブルを参照させるかを指定することができます。
「さまざまな条件」とは、例えば
といったものです。fwmarkとは、iptables(試したことはないですがebtablesでもできると思います)で--set-markで印をつけたパケットをひっかけるためのもので、iptablesの世界の柔軟なルールを使ってパケットにマーキングをし、それに応じてルーティングまでも切り替えられるというかなり(いい意味で)変態なものです。
話を元に戻します。
まず、なぜeth1から来たパケットがeth0から出ていくのか、iproute2のコマンドを使ってrule tableやroutingの情報を確認してみます。
rule tableはip ruleコマンドで確認できます。
# ip rule show 0: from all lookup 255 32766: from all lookup main 32767: from all lookup default
左の数値はruleの優先順位で、数値の小さい順に参照されます。
from allはこのrule内のrouteを参照するかどうかの条件です。
lookupのあとの255かmainがrule tableのIDです。基本的にIDは数値なのですが、/etc/iproute2/rt_tablesにIDと文字列の対応を書いておく(/etc/hostsやservicesの様なものです)と、文字列で表示されるようになります。
255のrule tableはループバックやブロードキャストのルーティングが定義されています。defaultはどのtableのroutingにもマッチしなかった場合に参照されるtableですが、多分中身は空だと思います。
で、mainが、いつもnetstat -nrやroute -nで見ているルーティングが定義されているtableです。ipコマンドでruleの中のルーティングを確認するには、ip routeコマンドを使います。netstat -nrと比較してみたり、255やdefault tableも見てみるといいと思います。
# ip route show table main 10.0.0.0/18 dev eth0 proto kernel scope link src 10.0.0.10 169.254.0.0/16 dev eth0 scope link default via 10.0.0.1 dev eth0
さて、10.0.0.11宛のパケットは、優先順位的に最初の255 tableの中のルーティングにはマッチしないので、続いてこのmain rule tableの中に入っていくわけですが、最初のルーティングルールにマッチし、そこには「dev eth0」とあるのでeth0から返りのパケットが出ていく、というあんばいなのがみてとれます。
というわけで、
- mainより優先順位が高いrule tableで、
- eth1から出ていくようなルーティングを定義
すればよさそうです。
rule tableの新規追加はip ruleコマンドでやります。
今回は、mainより優先ということで優先度200、tableのIDは100で、出るパケットの送信元アドレスがeth1の10.0.0.11のをひっかけるようなtableを作ります。
# ip rule add from 10.0.0.11 table 100 prio 200
そしてこのtableの中に、eth1から出ていくようなルーティングをip routeコマンドで定義します。
# ip route add dev eth1 src 10.0.0.11 table 100
これで10.0.0.11(eth1)宛のパケットもちゃんと返せるようになります。
ちなみに、どのrouteで出て行くか確認するには、ip route get IPADDRESS すればわかります。
落穂ひろい
今回はid 100というrule tableを作りました。途中でも触れましたが、/etc/iproute2/rt_tables に
100 ENI
とか書いておくと、ip rule showで数値でなく文字列(ENI)で表示されるので、わかりやすい名前を与えておくといいと思います。
ipコマンドのサブコマンドは特定できる文字まで省略できます。
例えば、ip route show table mainは、ip ro s t mainと書くこともできます。
この例はちょっとアレですが、ifconfig -a相当の情報はip addrで表示できるのですが、ip aと打つのはよくやります。