Linux を UPnP 対応ルーターにする (CentOS 5.6) : Linux の使い方


CentOS 5.6 を UPnP に対応した Linux ルーターにする

これまで PC ルーターとして運用していた CentOS 5.6 を UPnP 対応にする必要が出てきました。

CentOS を PC ルーターとして利用する方法については、これまでに以下のお話のなかで触れてきていましたが、今回はそのようにして構築した環境に UPnP 対応のための環境を追加したいと思います。

linux-igd をインストールする

Linux を UPnP 対応化するためには "linux-igd" というソフトウェアをインストールする必要があるようです。

"linux-igd" は、平成 23 年 9 月 3 日現在、http://linux-igd.sourceforge.net/ からダウンロードすることができるようになっていましたので、ここから、現時点での最新版である "linuxigd-1.0.tar.gz" をダウンロードして、今回は "/usr/local/src" ディレクトリに保存しておくことにします。

また、併せて "Intel UPnP SDK" もダウンロードしておきます。

こちらは http://upnp.sourceforge.net/ からダウンロードすることができるようでしたが、CentOS のパッケージ管理ツール "yum" でもインストールすることができるようになっていましたので、今回は次のようにして yum からインストールしておくことにしました。

yum install libupnp

yum install libupnp-devel

 

"linux-idg" のインストールの準備ができたら、次のようにしてインストールを行います。

cd /usr/local/src

tar xvzf linuxigd-1.0.tar.gz

 

make

make install

これで "linux-igd" のインストールが行えました。

ただし、起動用スクリプトはインストールされない様子でした。ソースコードを展開したディレクトリの中には起動用スクリプトと設定ファイルとが用意されていますので、それを次のようにして、所定の場所へコピーします。

cp -i etc/upnpd.rc /etc/rc.d/init.d/upnpd

cp -i etc/upnpd.rc.conf /etc/sysconfig/upnpd

これで、起動スクリプトと関係する設定ファイルとが、CentOS の起動スクリプトの所定のディレクトリへコピーされました。

起動スクリプトの準備ができたら、次のようにしてそれをシステムに登録します。

chkconfig --add upnpd

chkconfig upnpd on

これで linux-igd が登録されて、Linux 起動時にスタートするようになりました。

linux-igd の設定ファイルを整える

■ /etc/sysconfig/upnpd

linux-igd が使用するネットワークインターフェイスを指定します。

設定ファイルは "/etc/sysconfig/upnpd" にインストールされているので、それを開いて該当箇所を、自身の環境に合わせて調整します。

EXTIFACE=ppp0

INTIFACE=eth0

このようにすることで、eth0 インターフェイスを内側として、ppp0 インターフェイスを外側として、linux-igd が認識するようになります。

ここで設定したインターフェイスの情報は、linux-igd がポートマッピングを行う際に iptables を使って FORWARD チェインと PREROUTING チェインとにアドレス変換のルールを書き込みますが、その時に指定されるインターフェイス名となるようでした。

 

■ /etc/linuxigd/gatedesc.xml

linux-igd をインストールすると、linux-igd に関する設定(応答)ファイルが "/etc/linuxigd" ディレクトリに作成されます。

この中の "gatedesc.xml" ファイルには、検出されるデバイスの名称や、固有の識別子(UUID)がテキストで記載されています。主な部分としては、インストール直後は次のようになっていました。

friendlyName Linux Internet Gateway Device
manufacturer Linux UPnP IGD Project
manufacturerURL http://linux-igd.sourceforge.net
UDN uuid:75802409-bccb-40e7-8e6c-fa095ecce13e

これらは、UPnP デバイスとして検出された場合に目に留まるので、分かりやすくするために、内容を適宜調整するのが良さそうです。

特に UDN のところについては、UUID というくらいですから、機器ごとに固有の値が設定されているのが望ましいように思います。

この UUID は乱数でつけられる訳ではないようで、例えばローカル環境内に 2 つ linux-igd をインストールすると、両方とも同じ UUID が設定される感じでしたので、数字を少し変えるなどして、他の Linux と重複しないようにすると良さそうです。

UDN の箇所は "gatedesc.xml" 内に 3 箇所あるので、それらに同じ SSID を設定しておきます。

linux-igd のログを分離する

linux-igd では、ログを syslog の "local6" ファシリティに出力するようです。

syslog では既定で、"local6" ファシリティのログは一般的なログと併せて "/var/log/messages" に記録されるようになっています。

linux-igd の動作状況を確認する上で、他のログと分離した方が把握しやすくなるので、syslog の設定ファイルに次のように記載して、linux-igd 関係のログは "/var/log/upnpd" に保存されるようにします。

# UPnP IGD messages

local6.!=debug    /var/log/messages

local6.*    /var/log/upnpd

この設定ファイルは、CentOS 5.6 までなら "/etc/syslogd.conf" 内に設定します。CentOS 6.0 からは syslog の設定ファイルの名前が変更になっているようで、"/etc/rsyslogd.conf" 内に記述する必要があるようでした。

syslog 設定ファイルの内容を更新したら、CentOS 5.6 では "service syslog restart" として、CentOS 6.0 では "service rsyslog restart" として、syslog を再起動すれば、linux-igd のログが "/var/log/upnpd" に記録されるようになります。

ルーティングの設定について

linux-igd の説明書によると、適切に動作させるために "route add -net 239.0.0.0 netmask 255.0.0.0 eth0" というようなルーティング設定を行う必要があると記されていました。

これについては、あらかじめ用意されていた起動スクリプトの中で必要に応じて追加される記述がありましたので、敢えて登録する必要はなさそうですけど、もし登録しておく必要があれば、CentOS の場合はたとえば eth0 が linux-igd が内部側として使用するインターフェイスであれば、"/etc/sysconfig/network-scripts/route-eth0" というファイルに、次の内容を追記します。

239.0.0.0/8 dev eth0

このようにすることで、"service network start" 等によってネットワークが開始された段階で、ルーティングが設定されるようになります。

linux-igd を起動する

これまでの起動の準備が整ったら、linux-igd の起動は簡単です。

次のような CentOS でお馴染みの起動方法で、linux-igd を起動することができます。

service upnpd start

これで linux-igd が起動して、UPnP が有効になりました。

linux-igd が起動するとすぐに UPnP の Advertisement が送信されるため、UPnP 対応クライアントでは、すぐに linux-igd の存在を検出してくれます。

 

Linux にファイアーウォールが設定されている場合

linux-igd のインストールマニュアルを見ると、パケットフィルタを使用している場合には、次のように通信を許可するようにと記されています。

iptables -t filter -I INPUT 1 -s 224.0.0.0/4 -j ACCEPT

iptables -t filter -I INPUT 1 -d 224.0.0.0/4 -j ACCEPT

ただ、自分のパケットフィルタの環境では、このルールを適用しただけでは上手く通信が行えませんでした。

 

そこでとりあえず linux-igd が確保しているポートを調べてみたところ、次のようになっていました。

1900/UDP 0.0.0.0 にバインドされています。
Well-known ポート名は "SSDP" です。
49152/TCP 0.0.0.0 にバインドされています。
ポート番号は基本的には動きませんが、使用中の場合は次のポートが使用されるようでした。
40947/UDP ローカルホスト (127.0.0.1) にバインドされていました。
起動の度にポート番号が変わる様子です。

このうち、最後の "40947/UDP" については、linux-igd を起動する度に違うポートが設定されるようでした。ただ、これについてはバインドアドレスが "127.0.0.1" になっているので、パケットフィルターを調整する上では、考慮しなくて良さそうです。

 

それ以外の "1900/UDP" については、何度 linux-igd を再起動しても同じでしたが、"49152/TCP" については起動時に値が若干変わることがあるようでした。

別の Linux にインストールした linux-igd でも同じような感じでしたので、おそらく、基本的には "49152/TCP" を使用して、それが使用中だった場合には "49153/TCP"、"49154/TCP" と、順次試して行く感じになっている様子です。

1)静的にフィルターを解放することを考えると

この感じから、仮に "49152/TCP" が確実に使用できると考えた場合、次のようなパケットフィルターを設定すれば、UPnP Internet Gateway として認識されることになります。

今回は CentOS 5.6 環境なので、"/etc/sysconfig/iptables" に、次のような設定を追記してあげます。

-A INPUT -i eth0 -p udp -s 224.0.0.0/4 -j ACCEPT

-A INPUT -i eth0 -p udp -d 224.0.0.0/4 -j ACCEPT

 

-A INPUT -i eth0 -p tcp --dport 49152 -j ACCEPT

-A OUTPUT -o eth0 -p tcp --sport 49152 -m state --state ESTABLISHED,RELATED -j ACCEPT

先ほどもお話した通り、"49152/TCP" が空いていない(linux-igd 再起動時の空き待ちを含む)場合、ポート番号が 49153, 49154, ... とずれこむので、このように設定する場合には、安全のため、それらいくつかのポートの解放設定も行っておくと安心かもしれません。

2)動的にフィルターを解放することを考えると

これをより確実なものにしたい場合は、次のような upnpd の待ち受けポートを確認してそれを解放するスクリプトを作成するのもありかもしれません。

/etc/rc.d/init.d/upnpd-iptables


    

例えばこれを "/etc/rc.d/init.d/upnpd-iptables" という名前で保存して、次のように実行権限を付与します。

chmod +x /etc/rc.d/init.d/upnpd-iptables

そして、Linux 起動時にも UPnP 用のポート解放がされるように、次のようにしてシステムにスクリプトを登録しておきます。

このとき、上記スクリプトには upnpd サービスも連動して起動するようにしているので、併せて upnpd サービス自体は起動時に実行されないようにしておきます。

chkconfig --add upnpd-iptables

chkconfig upnpd-iptables on

 

chkconfig upnpd off

upnpd を upnpd-iptables の中で制御するようにしているのは、upnpd が起動した直後に UPnP Advertisement を送信するため、それに間に合うようにパケットフィルターを解放するためです。

upnpd が起動した直後に upnpd-iptables が起動できれば、UPnP Advertisement の応答になんとか間に合うようだったので、このような形で制御するようにしておきました。

 

また、このスクリプトでは UPnP の通信を許可するためのパケットフィルタを "INPUT_UPNP_SERVER" と "OUTPUT_UPNP_SERVER" という 2 つのチェインに登録するようにしてあるので、これらのチェインを iptables に登録しておきます。

CentOS 5.6 の場合は "/etc/sysconfig/iptables" に設定ファイルがありますので、そこの冒頭あたり、":OUTPUT ACCEPT [0:0]" といったチェインの定義の最後に続けて、次のような記載を追加します。

:INPUT_UPNP_SERVER - [0:0]

:OUTPUT_UPNP_SERVER - [0:0]

 

# UPnP Service

-A INPUT -j INPUT_UPNP_SERVER

-A OUTPUT -j OUTPUT_UPNP_SERVER

 

このようにしたら、次のようにして、いったん iptables を再起動すれば、"INPUT_UPNP_SERVER" と "OUTPUT_UPNP_SERVER" という 2 つのチェインが有効になります。

service iptables restart

チェインが有効になったら、後は upnpd が起動している状態で次のように実行すれば、linux-igd の待ち受けポート状態に応じて、パケットフィルタが追加されます。

service upnpd-iptables restart

linux-igd 起動時の注意事項

上記のようにパケットフィルタが有効な環境で linux-igd を起動させるには、linux-igd 起動時には次の点に注意する必要があります。

  1. linux-igd が起動していること
  2. パケットフィルターで linux-igd が使用するポートが解放されていること

これらを満たしていないと linux-igd が正しく応答できないのですが、今回用意したスクリプトは、なるべく既存のものには触れないように注意して作成したため、関係するスクリプトは "upnpd", "iptables", "upnpd-iptables" の 3 つですが、これらの整合性が重要になります。

 

まず "upnpd" がスタートしていることが重要です。これがスタートしていないと "upnpd-iptables" が linux-igd で使用するポートが特定できないので、upnpd-iptables にとって必須になります。

そして、もし途中で upnpd がリスタートされてポートが変わった場合には、再度 upnpd-iptables で、linux-igd が使用するポートを再度特定して、パケットフィルターを調整する必要があります。

そのため、今回は upnpd-iptables で upnpd のコントロールも併せて行うようにしています。よって、upnpd を再起動したいような場合でも、必ず "service upnpd-iptables restart" というように upnpd-iptables の方をコントロールしてあげるようにします。

 

それともう一つ注意が必要なのが、パケットフィルターを調整したような場合です。

この時、"service iptables restart" を実行すると、"upnpd-iptables" で設定されたパケットフィルターが消去されてしまいます。

そのため、パケットフィルターを調整した場合には、再度 "service upnpd-iptables" を実行して linux-igd で使用するポートを解放し直す必要があります。

 

まとめると、次の点に注意して運用して行く感じでしょうか。

  1. upnpd を再起動したい場合は upnpd-iptables を再起動する。
  2. iptables を再起動したら upnpd-iptables も忘れずに再起動する。

特に iptables の再起動については忘れがちになりそうなので、既定の起動スクリプト "/etc/rc.d/init.d/iptables" に手を加えても良い場合には、その中で upnpd-iptables サービスの起動も行うように設定するのが良いかもしれません。

または、次のように "/etc/rc.d/init.d/firewall" を作成して、iptables と upnpd-iptables の両方を 1 度にコントロールできるようにするのも良いかもしれません。

/etc/rc.d/init.d/firewall


    

これに、次のように実行権限をつけておきます。

chmod +x /etc/rc.d/init.d/firewall

これで、iptables の設定ファイルを編集した後には、次のようなコマンドを 1 つ実行すれば済むようになります。

service firewall restart

これで iptables と upnp-iptables の両方が再起動されるようになるので、いちおう手間は少なくなります。

どのような運用方法が理想的かは判らないですけど、ともあれいくつかのサービスを連携させるとなると、調整の時には何かと気を使う必要が出てきますね。

linux-igd が使用する iptables チェインの調整

linux-igd では、"/etc/upnpd.conf" 内の "forward_chain_name" と "prerouting_chain_name" の設定を調整することで、linux-igd が生成するアドレス変換ルールを変更することができるようになっています。

既定では "FORWARD" と "PREROUTING" とが設定されていますが、これらは通常のルール管理のメインで使うチェインのため、ここに UPnP で設定されるアドレス変換が混ざってくるとあまりきれいではない感じもします。

そこで、次のように調整してみることにしました。

forward_chain_name = UPNP_FORWARD

prerouting_chain_name = UPNP_PREROUTING

このように設定したら、iptables の設定ファイル "/etc/sysconfig/iptables" の方にも、次のようなイメージで、これらのチェインを登録します。

*filter

 

:UPNP_FORWARD - [0:0]

 

 

-A FORWARD -j UPNP_FORWARD

 

 

 

*nat

:UPNP_PREROUTING - [0:0]

 

 

-A PREROUTING -j UPNP_PREROUTING

 

このような感じで linux-igd 用のチェインとして "UPNP_FORWARD" と "UPNP_PREROUTING" を用意しておけば、既存のルールと UPnP で追加されたルールとが混ざらなくなりました。

この方法なら "UPNP_FORWARD" や "UPNP_PREROUTING" チェインを実行するタイミングや条件などにも独自のルール付けをすることができるので、都合によってはこういったチェイン分けを検討するのも良いかもしれないです。