【RaspberryPi】ネットワークインターフェースに複数のIPアドレス設定する

LAN内にあるデバイスがLAN内のポート80にのみアクセスができるようなものが存在します。そのポート自体の変更はできないので、アクセスされる側での対応を求められます。ただ、80番ポートはHTTPで使用されることも多いので、できれば開けておくことができれば後々便利かもしれません。そこでいろいろ調べてみたのですが、Linuxなどではネットワークインターフェース(以降NIC)に複数のIPアドレスを設定する(セカンダリIP)ことができるようです。

今回はRaspberryPiの無線LANNIC(WLAN0)へセカンダリIPを設定し、ポートフォワードを行ってプライマリIP側の別のポートで受信待ちをしているプロセスに処理をさせようと思います。

今回の設定を図に表すと以下のようになります。

f:id:ueponx:20200817221506p:plain

セカンダリIPを設定する

IPの設定を行う場合にはifconfigコマンドipコマンドの2つの方法が考えられます。現在はipコマンドの方が推奨されているのでそちらを使用してみます。 今回はwlan0にセカンダリIP(192.168.0.100)を設定します。

追加する場合

$ sudo ip addr add 192.168.0.100/24 dev wlan0

コマンドを実行すると、この様に追加されます。※今回はプライマリIPは192.168.0.7となっています。

設定前 f:id:ueponx:20200817114944p:plain

設定後 f:id:ueponx:20200817115227p:plain

実際にpingでアクセスを確認してみます。

f:id:ueponx:20200817131437p:plain

ちなみにこの設定を削除する場合には以下の様に行います。

削除する場合

$ sudo ip addr del 192.168.0.100/24 dev wlan0

注意点ですが

  • ipコマンドで追加の作業をしてもセカンダリのIPは保存されません。再起動すると初期化されてしまいます。
  • rc.localなどのファイルでコマンドの実行を行うことも考えられますが、DHCPでのIP取得より先にこのコマンド処理が行われるので、プライマリとセカンダリの対応は逆転します。後述のポートフォワードの処理もうまく行かないようです。

ipコマンドのサブコマンドにはip addr saveをつかうことでセーブはできるようなのですが、データの保存だけで永続的にしようできるようにはなっていないのでした。多分servicectrlなどを使用して、細かい設定を行えばできるかもしれません。情報が少なかったので調べられなかったのが残念です。(ちなみに保存した設定の復元はip addr restoreを使います)

ポートフォワードを使用してセカンダリIPの80番ポートを8000番ポートに転送する

iptablesコマンドを使用することで設定を行うことができます。

セカンダリIPのポート80からプライマリIPのポート8000へリダイレクトを行う設定

$ sudo iptables -A PREROUTING -t nat -p tcp -m tcp -d 192.168.0.100 --dport 80 -j REDIRECT --to-port 8000

このコマンドを実行することで192.168.0.100の80番ポートへのアクセスをプライマリIPの8000番ポートへ転送を行っています。

こちらの設定も再起動などを行うと消えてしまいます。その場合は以下の手順を行うことで設定を永続的にさせることができます。

iptablesコマンドの設定を永続化させる

iptablesコマンドの設定を永続化させるにはiptables-persistentというパッケージが必要です。以下を実行してインストールを行います。

$ sudo apt update
$ sudo apt install iptables-persistent

インストール中に、「現在のIpv4ルールを保存しますか?」と表示されたら【はい】を選択します。

f:id:ueponx:20200817005649p:plain

続けて「現在のIpv6ルールを保存しますか?」と表示されたら、【はい】を選択します。

f:id:ueponx:20200817005704p:plain

インストール前に既に設定が終わっていたら、これで設定は永続化の設定は完了しています。 もし、設定がされていないようであれば以下のコマンドで設定を保存します。

$ sudo /sbin/iptables-save > /etc/iptables/rules.v4

永続化が行われていれば、Raspberry Piを再起動しても設定はそのまま保存されています。

ちなみに保存された設定は以下のようになっていました。

~$ cat /etc/iptables/rules.v4 
# Generated by xtables-save v1.8.2 on Mon Aug 17 00:52:57 2020
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A PREROUTING -d 192.168.0.100/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8000
COMMIT
# Completed on Mon Aug 17 00:52:57 2020
# Generated by xtables-save v1.8.2 on Mon Aug 17 00:52:57 2020
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Mon Aug 17 00:52:57 2020

これでポートフォワードの設定は完了しました。

念の為今回の設定をシェルスクリプトにしたものが以下になります。 IPアドレスが設定されているかは確認して、設定済みであればなにもしないようにしています。

設定スクリプト

#!/bin/bash

## IPが含まれるかどうか調べる
if [ "`hostname -I | grep '192.168.0.100'`" ]; then
        echo "設定済のため処理終了"
else
        echo "未設定のため設定実行"
        sudo /sbin/ip addr add 192.168.0.100/24 dev wlan0
        sudo /usr/sbin/iptables -A PREROUTING -t nat -p tcp -m tcp -d 192.168.0.100 --dport 80 -j REDIRECT --to-port 8000
fi

動作をテストしてみる

あとは動作のテストを行ってみます。ポート番号を指定する簡単なHTTPサーバはワンライナーpython3 -m http.server 8000こんな風に 書くことができますが、今回はIPとポート番号を指定する必要があるので、pythonでのコードを書く必要があります。(多分)

server.py

import http.server
import socketserver

IP = '192.168.0.7' # RaspberryPiのプライマリIPアドレス
PORT = 8000 # ポートフォワード先

Handler = http.server.SimpleHTTPRequestHandler

httpd = socketserver.TCPServer((IP, PORT), Handler)

print('serving at IP', IP)
print('serving at port', PORT)
httpd.serve_forever()

こちらのコードを以下のように実行して

$ python3 server.py

WebブラウザからセカンダリIPへHTTPのアクセスを行うと以下のような画面になります。

コンソール画面

f:id:ueponx:20200817131530p:plain

ブラウザ画面

f:id:ueponx:20200817132724p:plain

これで無事にセカンダリIPに対してHTTPの処理をポートフォワードすることができました。

おわりに

これで、1つのNICに複数のIPアドレスを割り当てて、更にポートフォワードで処理を行うことができました。 LAN内のポート80にのみアクセスできるデバイスに関してもこれでなんとか対応できそうです。 今回のポートフォワードを行ってしまえばsudo権限でないと扱うことができなかったwell-known portへのアクセス対応も容易に可能です。

これまで、1つのネットワークインターフェースに複数のIPを振ることができるとは全く思っていませんでした。 ネットを検索しているとWindowsなどでもできるというような記述もありましたのでいつか試してみようかな。

ちなみに、今回問題考えていたLAN内のポート80にのみアクセスできるデバイスとは、Amazon Echoとなります。 次回以降でAmazon Echoを使った音声認識を行っていこうと思います。

/* -----codeの行番号----- */