search
LoginSignup
16
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

我が家のネットワークが不調すぎるのでRasberry PiでIPv6ルーターを自作した

背景

我が家のネットワークの調子が悪い。

ミーティング中やゲーム中に突如回線落ちする。ひどいときは日に3回ほどネットが切断される。どうやら利用しているルーターの負荷が高まると勝手に再起動し、その間に通信ができないということがわかった。

しょうがないので、ちょっと高めのIPv6対応ルーターを導入することにした。
東京住まいということもあり、PPPoE通信すると夜は画像すら満足にダウンロードできないレベルで遅い。IPoE接続(v6プラス)のルーターは必須だ。

Amazonでルーターを購入し利用しはじめて数日、新ルーターは性能が低いのかIPoEでも速度がでないことに気がついた。ネットの調子悪いのか?と思い昔のルーターに戻して通信したら爆速。まじか、せっかく高い金払ったのに通信遅かったら意味ないじゃん。

こうなったら、ルーターを自分で作るしか無い。
Rasberry Pi4を使って自分でルーターを作ることに決めた。

構築するネットワーク

構築するネットワークは以下の通り。

スクリーンショット 2020-08-16 20.18.30.png

我が家のISPは@Niftyで、IPv6プラスオプションを申し込んでいる。

IPv6のインターネット接続は簡単、単にイーサネットケーブルを直接ONUに繋げばよい。
ただ、自宅内のすべての端末がグローバルネットワークにダイレクト接続されるのはセキュリティの面で怖い。そのため、自宅内はIPv6プライベートネットワーク(fd00::/64)を組み、ルーターがIPマスカレードしてグローバルなネットワークにでていく構成とした。
IPv6の経路構築とIP割当は(DHCPv6ではなく)RAで行う。DNSの通知も今回RAにまかせることとした。

問題はIPv4ネットワークへの接続だ。IPv4 インターネットを利用するためには、JPNE(IPoE提供事業者)のBR(Border Relay)にトンネルを張り、途中までIPv6で通信しBRでIPv4にでていく必要がある。JPNEの場合、MAP-E方式でIPv4 over IPv6を実現しているとのこと。
自宅内のIPv4はプライベートネットワーク(10.0.0.0/8) を組み、IPアドレスの配布とDNS通知はDHCPで行う。
プライベートネットワークから外部に通信する際は、ルーターが一度通信を受け止め、BRへのトンネルを経由してインターネットとの通信を行う。

Rasberry Piのセットアップ

Rasberry Piは 4のModelB ( https://www.raspberrypi.org/products/raspberry-pi-4-model-b/ )を使うことにした。こいつをルーターにする。OSはUbuntu Server 20.04LTS。
Rasberry Pi はLANコネクタは1つしかないので、別途USB接続のLANアダプタを購入し接続した。これで eth0 と eth1 の2つができた。
今回はeth0をWANネットワークとし、eth1をLANネットワークとする。

EfikpA7UYAAGjyF.jpg

Rasberry PiへのOSインストールなどは省略する。
ルーターにするために必要なパッケージはあらかじめ以下コマンドでインストールしておく。

# IPv6 Router Advertisement(RA) のために利用
$ sudo apt install radvd

# IPv4 DHCPのためにインストール
$ sudo apt install isc-dhcp-server

IPv6の世界へRasberry Piを接続する

まずは現状のnetplanを確認。
eth0はONUに直接つなぎ、IPv6の世界へ接続する。
eth1は何も接続していない。

# cat /etc/netplan/50-cloud-init.yaml

network:
    ethernets:
        eth0:
            dhcp4: true
            optional: true
    version: 2

この状態でping6をかけてみる。
2404:6800:4004:80b::200e は youtube.com だ。

ubuntu@rasp-server:~$ ping6 2404:6800:4004:80b::200e
PING 2404:6800:4004:80b::200e(2404:6800:4004:80b::200e) 56 data bytes
64 bytes from 2404:6800:4004:80b::200e: icmp_seq=1 ttl=115 time=7.31ms

無事IPv6での通信が行えた。

DNSが無いため名前解決はできない。そのため、ping6 youtube.com コマンドを入力してもレスポンスは返ってこない。

また、IPv4の通信も当然ながらできない。

ubuntu@rasp-server:~$ ping 163.44.168.xxx
ping: connect: Network is unreachable

ルーターIP割当とRA/DHCPの設定

次にnetplanを変更し、eth0にWAN用の設定をし、eth1にプライベートネットワークルーターとしてのIPを設定する。
eth0はRAを受け取り、グローバルなIPv6アドレスをもらうようにする。

eth1はプライベートネットワークのゲートウェイになるため、IPアドレスは固定化しておく。
DNSは自前でサービスを立てず、Googleの8.8.8.8を利用することにした。

$ cat /etc/netplan/50-cloud-init.yaml

network:
    ethernets:
        eth0:
            dhcp4: true
            dhcp6: false
            accept-ra: yes
            optional: true
        eth1:
            dhcp4: false
            dhcp6: false
            addresses:
                - 10.0.0.1/8
                - fd00::1/64
            gateway4: 10.0.0.1
            gateway6: fd00::1
            nameservers:
                addresses:
                     - 8.8.8.8
                     - 8.8.4.4
                     - 2001:4860:4860::8888
                     - 2001:4860:4860::8844
    version: 2

プライベートネットワーク内にIPv4を配るため、(IPv4の)DHCPの設定をする。
10.0.0.0/8 のネットワークをプライベートとして利用することとした。

$ sudo vim /etc/dhcp/dhcpd.conf

# option domain-name "example.org";
# option domain-name-servers ns1.example.org, ns2.example.org;
## 上記はいらないのでコメントアウト


# 以下有効にする
ddns-update-style none;


# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
# 以下も有効にする
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
# ログも有効にする
log-facility local7;


# 10.0.0.0 のローカルネットワーク構築
subnet 10.0.0.0 netmask 255.0.0.0 {
  range 10.0.0.20 10.0.255.254;                  # DHCPとして割り当てるIPアドレスの範囲
  option routers 10.0.0.1;                       # ルーターのアドレス=自分のアドレス
  option broadcast-address 10.255.255.255;
  option domain-name-servers 8.8.8.8, 8.8.4.4;   # DNSはgoogleを使う
  default-lease-time 432000;                     # IPリース時間は長めに取る
  max-lease-time 864000;
}

プライベートネットワーク内のIPv6経路とアドレス割当のため、RAを設定する。以下のようにradvd.confを追加する。

$ cat /etc/radvd.conf

interface eth1 {             # インターフェースはeth1(LAN)
  AdvSendAdvert on;
  AdvManagedFlag off;        # Mフラグ Off
  AdvOtherConfigFlag off;    # Oフラグ Off
  MinRtrAdvInterval 30;
  MaxRtrAdvInterval 100;

  prefix fd00::/64 {        # fd00::/64 プライベートネットワークに通知
    AdvOnLink on;
    AdvRouterAddr on;
    AdvAutonomous on;
  };

  RDNSS 2001:4860:4860::8888 2001:4860:4860::8844   # GoogleのDNSを使う
  {
  };
};

ここまで構築したら、設定をすべて反映する

sudo netplan try

sudo systemctl enable isc-dhcp-server
sudo systemctl enable radvd

sudo reboot # 念の為サーバーを再起動

反映が終わったら、eth1を内部スイッチにつなぎ、他のPCと接続する。
今回はiMacをeth1側につなぎ、DHCPで割り振ったIPの更新を行った。

結果、以下のようにプライベートIPとDNSが振られていることがわかる。

スクリーンショット 2020-08-16 18.28.21.png

スクリーンショット 2020-08-16 18.28.27.png

IPは割り当てられているが、この状態でIPv6のインターネットにも接続はできない。

IPv6をフォワードさせる

プライベートネットワークから外のネットワークにでていくためには、ルーターがIPv4/IPv6をフォワードする必要がある。
そのため、/etc/sysctl.conf を以下のように修正しForward設定を有効化する。

# /etc/sysctl.conf

# Uncomment the next line to enable packet forwarding for IPv4
# IPv4をforwardするためにコメントアウトする
net.ipv4.ip_forward=1


# Uncomment the next line to enable packet forwarding for IPv6
#  Enabling this option disables Stateless Address Autoconfiguration
#  based on Router Advertisements for this host
# IPv6をforwardするためにコメントアウトする
net.ipv6.conf.all.forwarding=1


net.ipv6.conf.eth0.accept_ra = 2  # WAN側 RAを受け付ける
net.ipv6.conf.eth1.accept_ra = 0
net.ipv6.conf.all.disable_ipv6 = 0         # IPv6有効化
net.ipv6.conf.default.disable_ipv6 = 0

上記の設定を行ったら、以下コマンドで反映する

sudo sysctl -p

次にip6tablesを使い、fd00::/64 (ローカルネットワーク)ソースの通信をIPマスカレードしてWAN側に流す。すべての通信が素通りだと怖いので、ローカル発信の通信のみ許可する。このスクリプトを ipv6_forward.sh として保存する

#!/bin/bash

WAN_DEVICE=eth0
LAN_DEVICE=eth1
LOCAL_SOURCE="fd00::/64"

ip6tables -F

# すべての通信を素通りだと怖いので MARKをつけてLAN内のものからのみ許可する
ip6tables -t nat -A PREROUTING -i $LAN_DEVICE -j MARK --set-mark 1000
ip6tables -t filter -A FORWARD -p tcp -m mark --mark 1000 -j ACCEPT
ip6tables -t filter -A FORWARD -p udp -m mark --mark 1000 -j ACCEPT
ip6tables -t filter -A FORWARD -m mark --mark 1000 -j ACCEPT

# SSHはローカルネットワークからのみ許可
ip6tables -A INPUT -p tcp -s $LOCAL_SOURCE --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j DROP

# fd00::/64 宛の通信は全部WANに流す
ip6tables -t nat -A POSTROUTING -s $LOCAL_SOURCE -o $WAN_DEVICE -j MASQUERADE
sudo chmod +x ./ipv6_forward.sh
sudo ./ipv6_forward.sh

しばらく時間をおいてから、iMacからcurlでyoutube.comに接続してみる。

[nishio@NPC] $ curl youtube.com -v                                                                                                                                                                                                                                                                                                                                      [~]
*   Trying 2404:6800:4004:81d::200e...
* TCP_NODELAY set
* Connected to youtube.com (2404:6800:4004:81d::200e) port 80 (#0)
> GET / HTTP/1.1
> Host: youtube.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Location: https://youtube.com/
< Content-Length: 0
< Date: Sun, 16 Aug 2020 09:38:36 GMT
< Content-Type: text/html
< Server: YouTube Frontend Proxy
< X-XSS-Protection: 0
<
* Connection #0 to host youtube.com left intact
* Closing connection 0

接続成功! しかし、YoutubeはIPv6に対応しているから見られるけど、あいかわらずIPv4のサイトは見られない状態。

Rasberry Pi ルーターを Map-EでIPv4の世界に接続する

問題のIPv4接続。これは一筋縄ではいかず、自分の契約しているISPや通信方式に依存した設定が必要となる。
JPNEのBRへの接続方法は非公開で、手軽に接続とはいかない。ただ、某大型掲示板に接続方法が記載されており(参考: https://gato.intaa.net/archives/13186 )、どこかの誰かが晒してくれたスクリプトを使い接続を行う。

まずはじめに、 http://ipv4.web.fc2.com/map-e.html にアクセスし、自分が取得できたIPv6アドレスを入力する。
するとCE、IPv4、PSIDが取得できるので、これを以下スクリプトに記載する。BRのアドレスは2chで拾った。

#!/bin/sh

BR='2404:9200:225:100::64'
CE='CEのアドレス'
IP4='自分のグローバルIPアドレス'
PSID='自分のPSID'
WANDEV='eth0'
LANDEV='eth1'
TUNDEV='ip6tunnel1'

ip -6 addr add $CE dev $WANDEV   # WAN側デバイスにCEアドレス割当
ip -6 tunnel add $TUNDEV mode ip4ip6 remote $BR local $CE dev $WANDEV encaplimit none # TunnelでBRに接続
ip link set dev $TUNDEV mtu 1460 # MTU設定
ip link set dev $TUNDEV up       # Tunnel有効化

# IPv4のデフォルトルートをトンネルしたDeviceにする
ip -4 route delete default
ip -4 route add default dev $TUNDEV

# Map-Eの転送許可追加
iptables -t nat -F

rule=1
while [ $rule -le 15  ] ; do
  mark=`expr $rule + 16`
  pn=`expr $rule - 1`
  portl=`expr $rule \* 4096 + $PSID \* 16`
  portr=`expr $portl + 15`

  # ローカルネットワークからの通信にマークをつけて転送許可
  iptables -t nat -A PREROUTING -i $LANDEV -m statistic --mode nth --every 15 --packet $pn -j MARK --set-mark $mark
  iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet $pn -j MARK --set-mark $mark

  iptables -t filter -A FORWARD -p icmp -m mark --mark $mark -j ACCEPT
  iptables -t filter -A FORWARD -p tcp -m mark --mark $mark -j ACCEPT
  iptables -t filter -A FORWARD -p udp -m mark --mark $mark -j ACCEPT

  # Map-Eのルールにしたがい通信を飛ばす このルールは自分の契約による
  iptables -t nat -A POSTROUTING -p icmp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
  iptables -t nat -A POSTROUTING -p tcp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr
  iptables -t nat -A POSTROUTING -p udp -o $TUNDEV -m mark --mark $mark -j SNAT --to $IP4:$portl-$portr

  rule=`expr $rule + 1`
done

iptables -t mangle -o $TUNDEV --insert FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo chmod +x ./ipv4_forward.sh
sudo ./ipv4_forward.sh

ここまで設定したら、しばらく時間をおいて再度iMacから通信をしてみる。

[nishio@NPC] $ curl densan-labs.net -v                                                                                                                                                                                                                                                                                                                                  [~]
*   Trying 163.44.168.152...
* TCP_NODELAY set
* Connected to densan-labs.net (163.44.168.152) port 80 (#0)
> GET / HTTP/1.1
> Host: densan-labs.net
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx
< Date: Sun, 16 Aug 2020 09:57:44 GMT
< Content-Type: text/html
< Content-Length: 178
< Connection: keep-alive
< Location: https://densan-labs.net/
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host densan-labs.net left intact
* Closing connection 0

IPv4で接続できた。

IPv6 で本当に通信できているか確認する

https://test-ipv6.com/index.html.ja_JPhttps://ipv6-test.com/ のサイトにアクセスすると、
通信がIPv6でできているか確認できる。
我が家のiMacから通信した結果は以下のようになった。

IPv6_test_-_IPv6_4_connectivity_and_speed_test.png

念の為、http://www.ipv6scanner.com/cgi-bin/main.py などでポートスキャンをかけ、余計なポートが空いていないかどうかのチェックもしておく。通信がすべてFilterされていれば安心だ。

これでルーターの完成。お疲れさまでした。

参考資料

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
16
Help us understand the problem. What are the problem?