ここでは、LinuxマシンをADSLルータとして使えるようにする。今回構築したネットワークは次のような構成である。
+-----------+ 192.168.0.1
+------------+ +------+ +------+
[Internet]-----+ ADSLモデム +--+ eth1 | Linux Box | eth0 |-----[LAN]
+------------+ +------+ +------+ (192.168.0.0/24)
+-----------+
作業の手順として、まずLinuxマシンからADSLモデムを使用してInternetにアクセスできる状態にし、次にLAN内のマシンからもインターネットにアクセスできるようにする。
元帥府が利用しているADSLサービスは、モデムを通してPPPoEでアクセスするタイプのものだったので、PPPoEクライアントソフトが必要だった。というわけでいつものようにaptを使ってインストール。
root@host# apt-get install pppoe
インストールが終わると、「pppoeconfコマンドで設定をしてね」と言われたので、pppoeconfを実行する。質問に答えていくと設定ファイル(/etc/ppp/peers/dsl-provider)が出来る、という仕組みである。
最初にどのNICにADSLモデムが繋がっているのか自動的に検出するのだが、なぜか最初はうまく見つからないと言われてしまった。何回かやり直しているうちに、eth1にモデムを検出。なんでだ?
接続ユーザー名やパスワードなどを設定し、そのほかの項目は適当に推奨されたほうを選択したが、「通知されたDNSサーバーをresolv.confに追加する」だけはNoにしておいた。そのうち自分をDNSサーバにするためである。
最後に、自動的に接続するかと聞かれたのでOKしたが、待ってても接続できなかったのでCTRL+Cで中断。手動で接続してみることにする。
root@host# pon dsl-provider
しかし、やっぱりエラーが出る。エラーメッセージを適当に調べていると、どうもカーネルでpppがサポートされていないからのようだ。ガーン。menuconfigしてみると、たしかに「PPP support」がOFFになっている。仕方なくカーネルを再コンパイルして再起動。起動すると、無事に接続できた(pppoeconfで起動時に自動的に接続するように設定してたので)。
ifconfigでppp0が出来ていることを見たり、プロバイダのDNSサーバにpingを打ってみて、応答があるかを見たりしてみる。うまくいっているようだ。
まず、eth0自体のIPアドレスは固定指定しておく必要があるので、/etc/network/interfacesを編集する。
# eth0の設定をdhcp取得から直接指定に変更
iface eth0 inet static
address 192.168.0.1
netmask 255.255.255.0
/etc/network/interfacesの変更を反映させるため、ネットワークインタフェースを再起動しておく。
root@host# /etc/init.d/networking restart
ここで、eth0のIPアドレスが変わる場合はsshは切れてしまう。しばらくしてから192.168.0.1に接続してみるとちゃんと繋がったので作業を続けた。
Linuxマシンがルータとして動作するためには、IPパケットを中継する機能をオンにして、かつIPマスカレードを設定する必要がある。
root@host# echo 1 > /proc/sys/net/ipv4/ip_forward IPフォワーディングを有効化
これでIPフォワーディングが有効になるが、この設定は再起動すると消えてしまう。Debianで再起動時に自動的にこの設定を行うためには、/etc/network/optionsを編集する。
ip_forward=yes
つぎに、IPマスカレードである。これは、ルータがパケットを外部に送出するときに送信元アドレスと外部からの応答パケットのあて先アドレスを書き換えることで、複数のマシンがひとつのIPアドレスで通信できる仕組みである。仕組みは複雑だが、設定はこれ1行でOK。
root@host# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o ppp0 -j MASQUERADE
設定が終わったら、早速LAN内の別のマシンが外と通信できるか実験する。テストに使用するマシンのTCP/IPの設定を変更し、デフォルトゲートウェイの設定をLinuxマシンのLAN内のIPアドレスに設定する。ここでは、192.168.0.1である。
ためしに、LAN内のWindowsマシンで、プロバイダのDNSサーバにpingを打ってみたり、ブラウザを立ち上げて適当なサイトを見てみる。…問題ないように見える。
これでも一応通常の使用には耐えるのだが、このままではすべてのパケットを中継してしまうので気持ちが悪い。不要なパケットは中継しないようにフィルタルールを設定しておくべきだろう。「iptablesによるパケットフィルタリング」で紹介したのと同じように、こちらも基本は「デフォルトオフ」の思想で設定する。
どのルールにも合致しないパケットは破棄する
root@host# iptables -P FORWARD DROP
外からLANに送られるパケットを調べるチェインを作成
root@host# iptables -N i_to_l
root@host# iptables -A FORWARD -i ppp0 -o eth0 -j i_to_l
すでに確立されたTCPコネクションのパケットは通過させる
root@host# iptables -A i_to_l -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
DNSサーバからの応答は通過させる
root@host# iptables -A i_to_l -p udp --sport domain -j ACCEPT
pingへの応答は通過させる
root@host# iptables -A i_to_l -p icmp --icmp-type echo-reply -j ACCPET
パケットが届かなかったことを示すicmpメッセージは通過させる
root@host# iptables -A i_to_l -p icmp --icmp-type destination-unreachable -j ACCPET
LANから外に送られるパケットを調べるチェインを作成
root@host# iptables -P FORWARD DROP
root@host# iptables -A FORWARD -i eth0 -o ppp0 -j l_to_i
LANから外に送られるパケットはすべて通過させる
root@host# iptables -A l_to_i -j ACCPET
最低限こんな感じでいいと思う。あとはお好みで検査を厳重に。最後に、フィルタルールは再起動すると消えてしまうので、設定が終わったら忘れずに
root@host# /etc/init.d/iptables save active
を実行して、ルールを記憶させておくこと。
ここまでの設定で、内部から外部にコネクションを張ったりする通信は問題なく出来るようになったのだが、外部からLAN内のマシンにアクセスしたい場合や、一部のネットワークゲームをやりたい場合などは、あて先が判断できないので通信が確立できない。市販のルータで「静的NAT」などといわれる設定をLinuxルータでも施さなければならない。ここでは例として、外部からのリモートデスクトップ接続(WindowsXPのリモートコントロール機能)を192.168.0.5に転送する設定をしてみる。
root@host# iptables -t nat -A PREROUTING -p tcp --dport 3389 -i ppp0 -j DNAT --to 192.168.0.5
注意が必要なのは、この設定はあくまでパケットのあて先を判断できるようにしただけ、ということである。実際に通信が出来るためには、さらにパケットがファイアウォールを通過できるようにしなければならない。
あて先ポートが3389のTCPパケットで、192.168.0.5向けの物は通過させる
root@host# iptables -A i_to_l -d 192.168.0.5 -p tcp --dport 3389 -j ACCEPT
しばらくして外出し、帰ってから普段どおりWindowsマシンでWebを見ようとしたらサイトが見つからないとエラーが。どのサイトでも出るのでおかしいと思い、そのマシンのデフォルトゲートウェイの設定を確認したらちゃんと192.168.0.1になっていた。とすれば原因はLinux側か、と考えて192.168.0.1にpingをかけても応答がない。
sshも繋がらないので仕方なく久しぶりにキーボードを引っ張り出してコンソールログインしてみると原因は比較的すぐ分かった。LAN側のIPアドレスがなぜか変わっていた。
IPアドレスをもとの192.168.0.1に戻したらまた正常に動くようになったが、ではなぜ勝手にIPアドレスが変わったのだろうか?
そのあとも原因を探って、原因はdhclientだとわかった。よく考えたら以前eth0はDHCPを使っていたので、dhclientが実行中のままだった。これがおそらくeth0のIPアドレスを変更していたのだろう。設定ファイルを書き換えただけではeth0は固定IPアドレスになっていなかったのである。dhclientをkillしたらそれ以降は勝手にIPアドレスが変わることはなくなった。
最後におまけ。これはLinuxがルータになる場合に特有の問題というわけではないのだが、ルータが外部との通信にPPPoEを使用している環境では、特定のウェブサイトが閲覧できない問題が発生する。これは、「パスMTU問題」といわれる問題で、詳しい説明は省くが、通信相手とTCPパケットのサイズ(MTU)の交渉がうまく出来ず、その結果サイズが大きくて分割禁止のパケットが届いてPPPoEの通信路に乗れず、通信が失敗するために起きるものである。
一言で言うと相手側に責任があるのだが、そうは言っても困るのはこっちなので、なんとか対策したい。対策には以下の呪文を唱えるといいらしい。
root@host# iptables -A FORWARD -o ppp0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
これによって、ルータが接続要求パケットの中の、「期待する応答パケットの最大サイズ」を適切な値に書き換えてから送信するので、たとえ分割禁止フラグを立てて応答パケットを送るサーバも通信できる範囲内のサイズのパケットで応答を返してくれるようになるのである。