ラズパイでiptablesを使って有線LANを中継するスイッチングハブ的なNATコンバータを作る
※ 当ページには【広告/PR】を含む場合があります。
2022/04/21

インターネットで「ラズパイ ルーター化」や「ラズパイ スイッチング化」検索すると、様々関連でやられている方々の技術記事を拝見できます。
ご家庭でのネットワーク事情も様々あり、ラズパイの使い方は似ているようで何処か違うので、一概に同じとは言えませんが、今回のこの記事でやりたいものは以下の模式図のような

ラズパイのインターネット中継機かの種類
目標に応じては、ネットワーク構築用のソフトウェアの補助が必要になってくるかも知れません。
ラズパイを「WiFiルーター」「WiFiアクセスポイント」化したい場合には、
ラズパイをアクセスポイント化することで、WiFi無線接続の出来る子機を複数台接続して、ラズパイを経由して外部のネットワークにアクセスすることが出来るようになります。
もう一つの方式はネットワークブリッジを構築するやり方です。
ラズパイのネットワークと、他のインターネットに接続させたいネットワーク同士をブリッジさせれば、比較的簡単にインターネットを共有できます。
つまりラズパイを「スイッチングハブ」化したいときに利用できるのが、ブリッジ方式の良いところですが、ラズパイには基本有線LANポートが1つしか無いので、物理的に複数台の有線LAN機を接続しようと思うと、USB-LAN変換デバイスや、スイッチングハブのようなハードを増設する必要もあるのが難点です。
簡単にネットワークブリッジを設定するためのツールとして有名なのが、
巷にはとても便利なネットワーキングツールが存在しているとはいえ、よりネットワークなどを理解するためには、今回のお題はとても良い課題といえます。
ということでラズパイに接続するデスクトップを1台に絞って、手動で
参考サイト
ラズパイをルーター化する
では冒頭で挙げていた今回目指す自宅のネットワーク構成の模式図を再喝します。

ここでは自宅に元からあるブロードバンドルーター機器から構築されたネットワークを、例として
192.168.0.0/24
普段はこのネットワークを使って複数のパソコンやスマホなどをWiFiや有線LAN接続で利用しています。
一般的に、任意のネットワークから、そことは別のネットワークにポートフォワードによってネットワーク間共有を行うことを考慮すると、別のネットワークの構築用にも別のルーター機器を用意する必要があります。
ということで今回は手持ちのラズパイをルーター化して、新規に
192.168.21.0/24
さらに
192.168.21.0/24
この「NATコンバータ」という意味は、上の模式図の示す通り、「
192.168.0.0/24
192.168.21.0/24
重要なことは、以下のようにこのラズパイ存在する2つのインターフェイス(
wlan0とeth0
wlan0 (WiFi):
192.168.0.2
eth0 (有線LANアダプタ):
192.168.21.1
ということで以降では、先に
wlan0
eth0
192.168.21.0/24
ラズパイのwlan0(WiFi)をIP固定する
ラズパイでインターネットに繋げる側のネットワークの設定を行っていきます。
ここらへんの作業は以前のブログの回で初期設定として取り上げていましたので詳細は省きます。
では
wlan0
192.168.0.2
/etc/dhcpcd.conf
#...中略
interface wlan0
static ip_address=192.168.0.2/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1
編集後にこの設定を反映させます。
$ sudo systemctl restart dhcpcd
これでインターネットに繋がるかを確認しておきましょう。
ラズパイのeth0をIP固定する
続いてラズパイをルーター化する前に、有線LANのインターフェイスである
eth0
このeth0には
192.168.21.0
192.168.21.1
再度
/etc/dhcpcd.conf
#...中略
denyinterfaces eth0
interface eth0
static ip_address=192.168.21.1/24
static routers=0.0.0.0
static domain_name_servers=0.0.0.0
ラズパイにDHCP・DNSサーバーを導入する
次にラズパイをルーター化するため、DHCPサーバおよびDNSサーバーを導入します。
$ sudo apt install -y isc-dhcp-server dnsmasq
今回はラズパイのDHCPサーバーでよく目にする
isc-dhcp-server
dnsmasq
本記事の内容ではラズパイに直接デスクトップを1台しか繋ぎませんが、今後の拡張を考えてDHCPで
192.168.21.2 ~ 192.168.21.99
以下のように
/etc/dhcp/dhcpd.conf
#...中略
#👇コメントアウト
#option domain-name "example.org";
#👇コメントアウト
#option domain-name-servers ns1.example.org, ns2.example.org;
#👇#を外して有効化
authoritative;
#👇追記
subnet 192.168.21.0 netmask 255.255.255.0 {
range 192.168.21.2 192.168.21.99;
option routers 192.168.21.1;
option domain-name-servers 192.168.21.1;
option broadcast-address 192.168.21.255;
ignore declines;
}
このDHCPサーバーの設定をeth0へ紐付けします。
#...中略
#👇ターゲットインターフェイスを指定
INTERFACESv4="eth0"
#...略
次にDNSサーバーも設定確認していきます。
/etc/dnsmasq.conf
#...中略
server=8.8.8.8
interface=eth0
以上、DHCP・DNSサーバーの設定を終えたら、isc-dhcp-serverとdnsmasqのサービスを常駐化させます。
$ sudo systemctl start isc-dhcp-server
$ sudo systemctl enable isc-dhcp-server
$ sudo systemctl start dnsmasq
$ sudo systemctl enable dnsmasq
ここで一度ラズパイを再起動して、DCHPサーバーおよびDNSサーバーが立ち上がるかを確認しておきましょう。
$ sudo systemctl status isc-dhcp-server
$ sudo systemctl status dnsmasq
後はポートフォワーディングの設定を適切に行うことでラズパイがルーター化します。
このポートフォワーディングを行うためのツールこそ、今回の記事の主題でもある
iptables
かなり奥深いソフトウェアですので、使い方を軽くまとめながら次の節で設定手順を説明します。
ここまでの参考サイト
iptablesコマンドでwlan0とeth0を正しくポートフォワードさせる
今回の話の肝として、
この節で基本的なiptablesコマンドの概要・用法を先に説明していきます。
なおiptablesコマンドを十分理解していらっしゃる方であれば、基礎的な内容をスキップしてもらって、
iptablesコマンドのインストール
デフォルトではiptablesはインストールされていないので、Raspberry Pi OSに導入する場合、
$ sudo apt install iptables
から使えるようになります。
iptablesのテーブルモデル
iptablesのルーティングモデルで最小構造の模式図を以下に示します。

今回の内容ではローカルプロセスでのパケット介入処理(
INPUT
OUTPUT
今回は
FORWARD
PREROUTING
POSTROUTING
FORWARDチェイン
まず最も理解のしやすい
FORWARD
このルールは発信元・
-i <インターフェイスやIPアドレス>
-o <インターフェイスやIPアドレス>
ACCEPT/DROP
FORWARDチェインを追加すると、LAN内でパケットがどのようにルーティングされるかを細かく制御することが可能です。
例えば、ネットワークのルーターのデフォルトゲートウェイをもつインターフェイスが
eth0
$ iptables -A FORWARD -i eth0 -j ACCEPT
$ iptables -A FORWARD -o eth0 -j ACCEPT
このルールで、デフォルトゲートウェイの所属する内部ネットワークへのアクセスが許可されます。
FORWARDの用法として良く見かけるのは、LANネットワーク内部で使用しているファイル共有などのローカルなサービスが誤って、外部に漏洩しないように
-j DROP
#👇外部(0.0.0.0/0)に出ようとする1335~1359番のTCPパケットを棄却
$ iptables -A FORWARD -j DROP -d 0.0.0.0/0 -p tcp --dport 1335:1359
#👇外部(0.0.0.0/0)に出ようとする4465番のUDPパケットを棄却
$ iptables -A FORWARD -j DROP -d 0.0.0.0/0 -p udp --dport 4465
なお、たまに使う
-m state --state
-m state --state NEW:
新規に開始するパケットに適用
-m state --state ESTABLISHED:
確立済みパケットに適用
-m state --state RELATED:
既存の接続に対するパケットに適用
-m state --state INVALID:
不当なパケット
POSTROUTING(SNAT)の設定作法
SNATは送信元(Source)のアドレスを変換するNATであり、POSTROUTINGチェインでルール付します。
POSTROUTINGチェインとはインターフェイスからパケットが送信される際の変換ルール決める、送信側デバイスの設定を記述します。
例えば、送信元のアドレスが
192.168.1.123
eth0
$ iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.123/24 -j MASQUERADE
この書き換えに使われるオプションとして、
-j MASQUERADE
MASQUERADEでは、例えばパケットがブロードバンドルーターから外部へ出る際に、送信アドレスがグローバルIPアドレスに自動で変換されます。
ということで、管理者がどのアドレスやポート番号に置き換えればよいのかを逐一判断する必要がなくなります。
マスカレードを使わないで、明示に発信元アドレスの書き換えを行う場合に
-j SNAT
POSTROUTINGでつかうパラメータとして、
-t nat:
NATテーブルを使用
-A POSTROUTING:
POSTROUTINGチェインを追加。
ネットワーク内部から外部へ出ていくパケットのソース側のIPのを書き換え
-s <発信元のIPアドレス>:
ソース側のアドレス
-j MASQUERADE/SNAT:
マスカレードor特定のIPでの書換え方法の指定
-o <出力先インターフェイス>:
パケットが出ていくインターフェイスを限定する際にインターフェイス名で指定。
今回はインターフェイスの対応が一対一で明らかであるので省略
をそれぞれ指定しています。
変更後の内容が反映されているか、natテーブルの
Chain POSTROUTING
$ sudo iptables -t nat -n -L
#...中略
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
#👇この行が追加されている
MASQUERADE all -- 192.168.1.0/24 0.0.0.0/0
#...以下略
ここでMASQUERADEよって
192.168.1.0/24
PREROUTING(DNAT)の設定作法
SNATの対になる変換にDNATが存在します。
DNATは送信先(Destination)のアドレスを変換するNATで、
PREROUTING
このPREROUTINGチェインによって、パケットの受信時にそのパケットを何処に転送すると良いのかがNATテーブルのルールによって変換されることになります。
例えば、wlan0インターフェイス入力されるパケットに対して、宛先が
192.168.1.2
192.168.1.123
$ sudo iptables -t nat -A PREROUTING -d 192.168.1.2 -i wlan0 -j DNAT --to 192.168.1.123
ここでもitablesに色々とパラメータが付いています。
-t nat:
NATテーブルを使用
-A PREROUTING:
PREROUTINGチェインを追加。
ネットワーク外部から内部へ入ってくるパケットのディスティネーション側のIPのを書き換え
-d <送信先のIPアドレス>:
ディスティネーション側のアドレス
-j DNAT:
DNATの使用
-i <入力先インターフェイス>:
パケットが入ってくるインターフェイスを限定する際にインターフェイス名で指定。
今回はインターフェイスの対応が一対一で明らかであるので省略
DNATを作成したら、NATテーブルを確認してみます。
$ sudo iptables -t nat -n -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
#👇この行が追加されている
DNAT all -- 0.0.0.0/0 192.168.1.1 to:192.168.1.123
#...省略
これでDNATがPREROUTINGテーブルに作成されていることが分かります。
iptablesコマンドからポートフォワーディング
では話を
前節ではネットワークインターフェイスのIP固定、DHCP/DNSサーバーの設定まで終わりました。
続きとして、
ラズパイの
eth0
eth0
ということで、eth0に割り振られていた
192.168.21.1
192.168.21.0
このデフォルトゲートウェイを基点に、別のネットワーク(
192.168.0.0
192.168.0.2
192.168.21.0
その「転送」のことを
ポートフォワード
ポートフォワード機能をラズパイ側に設定していない状態のままであれば、接続されたPCからのリクエストを何処に転送していいのか分からないままなので、
192.168.21.0
ということでこのポートフォワードを有効化してみます。
/etc/sysctl.conf
net.ipv4.ip_forward=1
#...中略
net.ipv4.ip_forward=1
#...以下略
この
/etc/sysctl.conf
$ sudo sysctl -p
これでラズパイのポートフォワード機能が開始されます。
rc.localにiptablesコマンドを書き込む
では今回の話で最も重要な設定を行います。
/etc/rc.local
#...中略
#👇exit0の手前に追記
iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
exit0
ここで最初2つの
FORWARD
「eth0から入ってきたパケットをwlan0へ転送する」
「wlan0から入ってきたパケットをeth0へ転送する」
これでwlan0とeth0が相互に通信されるようになります。
最後の3つめの
POSTROUTING
「ラズパイから送られる全てのパケットの送信元をwlan0として扱う」
このルールが無いと、外部に繋がっている
192.168.0.0
eth0
192.168.0.0
192.168.0.1
よって、SNATとしてラズパイから出ていく全てのパケットの送信元をwlan0(
192.168.0.2
これで
rc.local
192.168.21.0
余談〜rc.localにiptablesコマンドを書き込まないNAT設定の保存方法
では、iptablesコマンドからSNAT/DNATを手動で設定した場合、基本的にはそのセッションだけのルーティングテーブルが構築できるだけですので、ラズパイを再起動するとこの設定はリセットされてしまいます。
このため、先程説明したようにテーブルの設定を永続化するためには、
/etc/rc.local
中には
/etc/rc.local
代替手段として、
iptables-save
iptables-restore
まずは手動で設定したルーティングテーブルの内容を下記のコマンドで保存しておきます。
$ sudo sh -c "iptables-save > /etc/iptables.ipv4.nat"
次に
/etc/rc.local
exit 0
#...中略
#👇exit0の手前に追加する
iptables-restore < /etc/iptables.ipv4.nat
exit 0
これでラズパイの再起動のたびに、
/etc/iptables.ipv4.nat
これで正常に動作していれば、無事ラズパイをイーサネットコンバーターとして利用できるようになっているはずです。
ここまでの内容で参考にさせていただいたサイト
実機でのラズパイルーター化の確認
折角ですので、自作ラズパイルーターの動作確認も行います。
ラズパイのeth0へ接続するパソコンのIP固定
早速ラズパイとクライアントとなるLinuxデスクトップPCを有線LANで接続して、外部のインターネットへ繋がるかを簡単にpingしてみます。
DHCPサーバーは機能しているのでパソコン側はIPを固定する必要がないのですが、一応固定化しておきましょう。
デスクトップPC側で、
/etc/dhcpcd.conf
interface enp3s0
static ip_address=192.168.21.2/24
static routers=192.168.21.1#👈ゲートウェイがラズパイのeth0
static domain_name_servers=192.168.21.1#👈DNSもラズパイのeth0
IP固定し終わったら、この内容をシステムに反映させるために、
$ sudo systemctl restart dhcpcd
とコマンドを叩きます。
なお、接続テストに用いたデスクトップのインターフェイスの名前は
enp3s0
以上、もろもろの準備が整ったのでラズパイへ有線LANを接続します。
ではまずはルーター化したラズパイで動作確認を行います。
#👇ラズパイのeth0
$ ping 192.168.21.1
#👇eth0に接続中のデスクトップ
$ ping 192.168.21.2
#👇インターネットに接続されている自宅ルーター
$ ping 192.168.0.1
#👇外部のインターネット
$ ping google.com
問題がなければ、これが全てパケットロス無しで通るはずです。
で、いよいよ本題のラズパイのeth0に接続したデスクトップから確認です。
#👇ラズパイのeth0
$ ping 192.168.21.1
#👇ラズパイのwlan0
$ ping 192.168.0.2
#👇インターネットに接続されている自宅ルーター
$ ping 192.168.0.1
#👇外部のインターネット
$ ping google.com
が全てping出来ていればラズパイがルーター化がされています。
異なるネットワーク間で通信を確立してSSH接続を試みる
上記の内容までで「ラズパイのルーター化」の目的は十分達成しているのですが、実際に使ってみると思わぬ落とし穴が存在しています。
例えば192.168.0.0ネットワークに所属しているパソコンA(
192.168.0.3
192.168.21.2
#👇パソコンBからパソコンAにpingしてみる
$ ping 192.168.0.3
#...通信可能
#👇パソコンAからパソコンBにpingしてみる
$ ping 192.168.21.2
#...到達しない!

というのは、
192.168.0.0
192.168.21.0
192.168.0.1
ですが、
192.168.0.1
192.168.21.0ネットワーク
これを回避するためには、パソコンAから
192.168.21.2
192.168.0.2
ですが、パソコンAが
当たり前ですが、パソコンAから同じネットワークにいるラズパイ(
192.168.0.2
#👇パソコンAから192.168.0.2にpingしてみる
$ ping 192.168.0.2
#...到達可能
これだと、
192.168.21.2
結論からいうと、パソコンAから別のネットワークにあるパソコンBには直接pingが出来ないものの、

例えば上の模式図のように、ラズパイ側のwlan0側にポート12345番(
192.168.0.2:12345
192.168.21.2:54321
$ sudo iptables -t nat -A PREROUTING -m tcp -p tcp -d 192.168.0.2 --dport 12345 -j DNAT --to 192.168.21.2:54321
とPREROUTINGチェインを定めると良いでしょう。
しかし、まだこのままだと宛先アドレスを書き換えるだけで、パソコンB(
192.168.21.2
192.168.0.3
返答する戻りの宛先は自分のデフォルトゲートウェイであるラズパイのeth0・
192.168.21.1
192.168.0.3
$ nc -v -w 1 192.168.0.2 -z 12345
nc: connect to 192.168.0.2 port 12345 (tcp) failed: Connection refused
なおpingだとポート指定できないので、ポートありの接続確認には
nc
ではどうすればパソコンBからの返答が棄却されずに、そのままパソコンAに送り出してもらえるのかというと、SNATのルールで、パソコンBのsshポート54321番(
192.168.21.2:54321
192.168.21.1
$ sudo iptables -t nat -A POSTROUTING -m tcp -p tcp -d 192.168.21.2 --dport 54321 -j SNAT --to 192.168.21.1
このSSH接続させるための許可をFORWARDチェインを追加します。
$ sudo iptables -A FORWARD -m tcp -p tcp -d 192.168.21.2 --dport 54321 -j ACCEPT
$ sudo iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
ここまでのルーティングテーブルを追加することで、パソコンAからのパソコンBに接続できているかを確かめてみると、
$ nc -v -w 1 192.168.0.2 -z 12345
Connection to 192.168.0.2 12345 port [tcp/*] succeeded!
と接続が成功しています!
例ではSSH(デフォルトではポート22番)を使いましたが、HTTP(デフォルトでポート80番)や他の代表的なポートでも同様の考え方で変換ルールを付与することで、異なったネットワーク間に接続することができます。
最後にここまでの
/etc/rc.local
#...省略
#👇exit0の手前に追加
iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
iptables -A FORWARD -m tcp -p tcp -d 192.168.21.2 --dport 54321 -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A PREROUTING -m tcp -p tcp -d 192.168.0.2 --dport 12345 -j DNAT --to 192.168.21.2:54321
sudo iptables -t nat -A POSTROUTING -m tcp -p tcp -d 192.168.21.2 --dport 54321 -j SNAT --to 192.168.21.1
exit0
ここまで来ると、iptablesもかなり複雑な設定と感じてしまいますが、ある程度使い方に慣れれば、地図を書くような感覚で結構楽しいと感じてしまいます。
ここまでで参考させていただいたサイト
まとめ
今回はiptablesコマンドを使ってルーターとルーティングテーブルの設定を色々とラズパイ上で弄ってみました。
普段はあまりルーティングテーブルなんてあまり意識することがないと思いますが、使い方を知っておくと非常に便利なツールですので、一度じっくり勉強されてみると新しい発見があるかも知れません。