(先程上げた記事にMACアドレス記載されていたので念の為上げ直します)
前提
以前こんな感じで、ミニPCにDebianをインストールしてルーターを作った
現在もPCでルーターが運用されている
MinisforumのミニPCがちょいちょいストレージのエラーかなんかで再マウントされ、リードオンリーになりルーターが機能しなくなるケースがあったので、ミニPCの選定で最終候補に残っていたもう片方のSkynew IN-1を結局買う
選定中より値上りしてしまっていた🥺
必要な手順やconfigについてはセットアップしやすいようにスクリプトを作っていたので、移行はそんなに手間取らずに完了できて良かった
問題が発生
以前からあったかもだけど
- LAN側からIPv4が通らないことがある
- LAN側からpingするとIPv4が優先される
という事象が見られた
IPv4が通らなくなることがあるのが解消できればLAN側でIPv4が優先されてても困ることはないのだけど、折角IPv6に対応してるのであればIPv6で通信したほうが良い
結論としては
【問題】LAN側からIPv4が通らないことがある
【解決?】MTUの値を調整したら解決した気がする
もうちょっと様子見てみないとなんともだけど、少なくとも調整してから何回かping打ってみて問題が解消されている
以前は何回か打てばpingが返ってこなくなる事象に遭遇することが頻繁にあった
【問題】LAN側からpingするとIPv4が優先される
【解決】gai.confの修正で解決した
/etc/gai.conf
を修正してやることでデフォルトでIPv6が優先されるようになった
詳しくは後述
調べたこと
そもそもMAP-Eの設定をした後にIPv4とIPv6でpingして疎通確認までスクリプトで行っているのだけど、そこでIPv4が失敗していることがあった
ここはもう大分安定していたんだけど、その経験ゆえに何か違和感あればping飛ばしてみる癖がついていた
そこでLAN側からping打つと返ってこないケースが見られ、その際にIPv4で名前解決されていた
ということは?
DNSについて確認する
ルーター側でsystemd-resolvedを利用しDNSサーバーにしている
LAN側では基本的にDNSサーバーをルーターのIPアドレスに指定する運用にしている
メインPCのUbuntuではsystemd-resolvedがスタブリゾルバとして動いており、そこでルーターのIPアドレスが指定されている
なので、 /etc/resolv.conf
自体は 127.0.0.53
となっているが実態としてはルーターに対して名前解決を行う
以下のコマンドを打つなどして挙動を確かめてみた
nslookup google.com
nslookup google.com 192.168.0.1
nslookup google.com fd00:0:dead:beef::1
dig A google.com
dig A google.com @192.168.0.1
dig A google.com @fd00:0:dead:beef::1
dig AAAA google.com
dig AAAA google.com @192.168.0.1
dig AAAA google.com @fd00:0:dead:beef::1
sudo resolvectl -p dns query google.com
DNSへの接続がIPv4だろうがIPv6だろうが、AレコードかAAAAレコードかの話だし、ここは何も問題なく想定通りにIPv4とIPv6が返ることを確認できた
pingを打つ
ping google.com
ping -4 google.com
ping -6 google.com
デフォルトでIPv4になってしまっていたが、IPv4では問題の事象に遭遇しなければping通るし、IPv6指定なら問題なく名前解決もpingも通った
これをルーターのPCでやるとデフォルトでもIPv6で名前解決されていた……
wgetしてみる
wget --no-proxy -O /dev/null https://www.google.com/
名前解決でIPv4とIPv6が表示されるがIPv4が利用される……
そしてIPv4が通らないケースに当たるとタイムアウトしてIPv6の通信で成功する状態となった
ラチがあかない
IPv6優先で調べるとすでにIPv6優先になっているとか、(そのため)IPv6を無効にするためにはとかの記事が多くでてくる
違う、そうじゃない
そもそもlocalhostに対してping打つと::1で名前解決されるのでIPv6が優先されないわけじゃないようだ
もっとも/etc/hostsに記載されているのでDNSサーバーに問い合わせしてるわけではないが
一方、pingが返ってこなくなる問題についてはどこかでパケットが闇に葬り去られている雰囲気
そして果たしてこれが原因かは不明だけど、どうやらPath MTU Discovery Black Holeなる問題があるらしい
こういうのはwiresharkとかでパケット解析するとわかるものなのだろうか
HTTPとかの平文パケットを読んでみたことはあるけれども……
色々ググっては試してみるがまったく解決には至らなかった
結果
まずIPv6優先の方から
ULAしか持っていないからIPv4が優先されるのかと思い、ipコマンドで適当にグローバルアドレスのプレフィックスに合わせたIPv6を振ってみたらpingでIPv6が利用されるようになった
こうなると話は早く、ULAがIPv4より優先される方法を調べればいい
この辺を参考にして /etc/gai.conf
で以下を記載した
label fc00::/7 1
precedence fc00::/7 40
precedence ::ffff:0:0/96 1
これで無事にIPv6が優先されるようになり、IPv6環境確認サイトでもいい感じになった
続いてIPv4の通信が失敗する方について
IPv6のMTUの最小値が1280ということでWAN側のデバイスのMTUとIPv4を通すトンネルデバイスのMTUを1280に設定してみた
これでとりあえず設定してからpingが返ってこなくなる事象を見ていないが対処法として良いのかが不明
MTU上げられるなら数値大きい方が良いですしね……
MAP-EでIPv4通信するので、実際の物理的なNICにはIPv6で通信してると思うので、そこのMTUを1280にすることでIPv4で起きる事象が解消されるのは特に違和感ないです
が、この辺は良くわかってないので巧い設定があれば知りたい……
ちなみに元々100MbpsのマンションのVDSLなのでMTUを1280にしてもスピードテストで特に変わらず80〜90Mbpsというところ
付録
前の記事に載せてたadd-rulesスクリプトは以下のように変更した
#!/bin/bash
function finish() {
cd - > /dev/null 2>&1
exit $1
}
cd $(dirname "$0")
NODE_PATH=$(which node)
if [ $? -ne 0 ]; then
echo $NODE_PATH
finish 1
fi
# LAN側のNIC
LAN_DEV=enp3s0
# WAN側のNIC
WAN_DEV=enp4s0
# TUNNELデバイス
TUN_DEV=tun0
# トンネルデバイスは落とす
ip link set $TUN_DEV down
# IPv6
WAN_IPv6=$(ip -6 addr show dev "$WAN_DEV" | grep inet6 | grep -v "inet6 fe" | sed -e 's/^ *inet6 \([^ ]*\).*/\1/')
# pppoeを落としておく
poff -a > /dev/null 2>&1
NODE_RET=$(node ./map-e.js "$WAN_IPv6")
if [ $? -ne 0 ]; then
echo "$NODE_RET"
finish 2
fi
echo $NODE_RET
eval "$NODE_RET"
# echo $BR
# echo $CE
# echo $IPv4
# echo $PSID
cat << YAML > /etc/netplan/00-init.yaml
network:
version: 2
renderer: networkd
ethernets:
# LAN
${LAN_DEV}:
dhcp4: false
dhcp6: true
accept-ra: true
mtu: 1500
addresses:
- 192.168.0.1/24
- fd00:0:dead:beef::1/64
match:
macaddress: 00:00:00:00:00:00
set-name: enp3s0
# WAN
${WAN_DEV}:
dhcp4: false
dhcp6: true
accept-ra: true
optional: true
mtu: 1280
addresses:
- ${CE}/64
match:
macaddress: 00:00:00:00:00:00
set-name: enp4s0
nameservers:
addresses:
- 2001:4860:4860::8888
- 2001:4860:4860::8844
YAML
# cat /etc/netplan/00-init.yaml
cat << YAML > /etc/netplan/10-ipoe-gw.yaml
network:
version: 2
tunnels:
${TUN_DEV}:
mode: ipip6
accept-ra: true
local: ${CE}
remote: ${BR}
mtu: 1280
addresses:
- ${IPv4}/32
routes:
- to: 0.0.0.0/0
scope: link
YAML
# cat /etc/netplan/10-ipoe-gw.yaml
netplan generate
sleep 1
ip addr flush ${TUN_DEV}
ip link set ${TUN_DEV} down && ip link set ${TUN_DEV} up
ip -4 route delete default dev ${TUN_DEV}
ip -4 route add default dev ${TUN_DEV}
ip -6 tunnel change ${TUN_DEV} encaplimit none
iptables -t nat -F
rule=1
while [ $rule -lt 16 ]; do
mark=$(($rule + 16))
pn=$(($rule - 1))
port_l=$(($rule * 4096 + $PSID * 16))
port_r=$(($port_l + 15))
iptables -t nat -A PREROUTING -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 nat -A POSTROUTING -p icmp -o $TUN_DEV -m mark --mark $mark -j SNAT --to $IPv4:$port_l-$port_r
iptables -t nat -A POSTROUTING -p tcp -o $TUN_DEV -m mark --mark $mark -j SNAT --to $IPv4:$port_l-$port_r
iptables -t nat -A POSTROUTING -p udp -o $TUN_DEV -m mark --mark $mark -j SNAT --to $IPv4:$port_l-$port_r
rule=$(($rule + 1))
done
iptables -t mangle -F
iptables -t mangle -o $TUN_DEV --insert FORWARD 1 -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:65495 -j TCPMSS --clamp-mss-to-pmtu
finish 0
これを呼び出すスクリプトは以下のようになっています
#!/bin/bash
# OSブート時に実行されるスクリプト
# rootユーザーによって実行される想定
function writelog() {
LOG_PATH=/dev/shm/startup.log
echo "[$(date "+%Y-%m-%d %H:%M:%S")] $1" >> $LOG_PATH
}
writelog "start-up スクリプトを始します"
cd $(dirname "$0")
TRY=0
RETRY_MAX=5
while [ true ]; do
writelog "map-e の適用処理を開始します"
# MAP-E適用
writelog "$(../map-e/add-rules 2>&1)"
netplan apply
writelog "www.iijmio.jp へ IPv4 PING を送信します"
ping -4 -n -c3 -W1 www.iijmio.jp > /dev/null 2>&1
IIJMIO_IPv4_PING=$?
writelog "www.iijmio.jp へ IPv6 PING を送信します"
ping -6 -n -c3 -W1 www.iijmio.jp > /dev/null 2>&1
IIJMIO_IPv6_PING=$?
writelog "google.com へ IPv4 PING を送信します"
ping -4 -n -c3 -W1 google.com > /dev/null 2>&1
GOOGLE_IPv4_PING=$?
writelog "google.com へ IPv6 PING を送信します"
ping -6 -n -c3 -W1 google.com > /dev/null 2>&1
GOOGLE_IPv6_PING=$?
IPv4_OK=0
IPv6_OK=0
if [ $IIJMIO_IPv4_PING -eq 0 -o $GOOGLE_IPv4_PING -eq 0 ]; then
IPv4_OK=1
fi
if [ $IIJMIO_IPv6_PING -eq 0 -o $GOOGLE_IPv6_PING -eq 0 ]; then
IPv6_OK=1
fi
if [ $IPv4_OK -eq 1 -a $IPv6_OK -eq 1 ]; then
writelog "PING 確認が成功しました! ($(($TRY + 1))回試行)"
writelog "ping result (www.iijmio.jp): $IIJMIO_IPv4_PING"
writelog "ping result (www.iijmio.jp [IPv6]): $IIJMIO_IPv6_PING"
writelog "ping result (google.com): $GOOGLE_IPv4_PING"
writelog "ping result (google.com [IPv6]): $GOOGLE_IPv6_PING"
break
else
if [ $TRY -ge $RETRY_MAX ]; then
writelog "PING 確認に失敗しました"
writelog "start-up スクリプトを停止します"
echo "$(
echo "ルーター起動スクリプトエラー"
echo
echo "map-eの設定に失敗しました"
echo "ping result (www.iijmio.jp): $IIJMIO_IPv4_PING"
echo "ping result (www.iijmio.jp [IPv6]): $IIJMIO_IPv6_PING"
echo "ping result (google.com): $GOOGLE_IPv4_PING"
echo "ping result (google.com [IPv6]): $GOOGLE_IPv6_PING"
)" | mail -s "ルーター起動スクリプトエラー" -a "From:alert@example.com" "$STARTUP_MAILTO"
exit 1
fi
writelog "$(($TRY + 1))回目失敗"
writelog "IPv4_OK: $IPv4_OK"
writelog "IPv6_OK: $IPv6_OK"
if [ $TRY -lt $RETRY_MAX ]; then
sleep $((2 ** $TRY))
fi
fi
TRY=$(($TRY + 1))
done
writelog "start-up スクリプトの全行程が完了いたしました"
色々触っててexecの方でnetplay applyするようにしてたのがそのままになっていた
add-rules側でnetplan applyまでする方が良いと思う
IPv4のping失敗は今回解決したIPv4に繋がらないケースがあるために発生していたと思われるので、リトライ処理も不要になるはずだが1発で終わるなら特に問題はないのでこれはそのままにしておく
それでは良いDebianライフを