nftablesを使ったLinuxルータの再起動後にクライアントのTCP通信を一度切る

目次

自宅のLinuxルータ(NATルータ)を再起動した後、ルータが起動してもしばらくクライアントの通信が復活しなくて悲しかったので対策をしてみた。

背景

自宅では UbuntuをMAP-Eルータにする試み で書いたようなnftablesを使ったLinuxルータでv6プラス(MAP-E)を処理してインターネットに接続している。
ときどきアップデートなどを当ててルータの再起動が必要だが、再起動をするとルータが保持しているセッション情報やNATテーブルが飛んでしまい、再起動前の通信は(再接続されるまで)通らなくなってしまう。
そして、各クライアントはこの状況を即座に知ることができないため、再送信を繰り返しタイムアウトが起きるまで通信ができなくなってしまう。

この状況を緩和するため、セッションが切れてしまっているTCPの通信については一度TCP RESETを送ってあげることで、クライアント側に接続が切れたことを認識させ、即座に再接続をしにいくようにしてみた。

環境

  • OS: Ubuntu Server 24.04 (前回の投稿時からアップデートした)

手順とか

今回は新規だがSYNが立っていないforwardingすべきTCP接続が来た場合に、送信元にRSTフラグを立てたパケットを返答して接続を切断させたい。
nftablesではこのようなパケットをinvalidとして扱うため、ルールはinvalidをdropするルールより上に書く必要がある。
インターネット側からの新規だがSYNが立っていない通信に対して何らかの応答を返すとあまり良くないため、iifnameを指定して内側からの通信にのみ応答している。

### forward rules
# forward - RST no-syn packets from internal
nft add rule inet filter forward iifname { $LANDEV } ip protocol tcp tcp flags \& \(fin \| syn \| rst \| ack\) != syn ct state new counter reject with tcp reset
nft add rule inet filter forward meta protocol ip6 meta l4proto tcp iifname { $LANDEV } tcp flags \& \(fin \| syn \| rst \| ack\) != syn ct state new counter reject with tcp reset

# forward - drop invalid
nft add rule inet filter forward ct state invalid counter drop

また、IPv4通信ではNATテーブルが消えてしまっているため救えないが、NATをしていないIPv6通信であればacceptすることで通信を継続させることもできる。
一度acceptで通信を通せば、以降はestablishedとして扱われる。

nft add rule inet filter forward meta protocol ip6 meta l4proto tcp iifname { $LANDEV } tcp flags \& \(fin \| syn \| rst \| ack\) != syn ct state new counter accept

nft list rulesetの形式で書くとこんな感じになる。

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                iifname "lo" accept
                ct state invalid counter drop
                ct state established,related counter accept
                counter drop
        }
        chain forward {
                type filter hook forward priority filter; policy drop;
                iifname "eth2" ip protocol tcp tcp flags != syn / fin,syn,rst,ack ct state new counter reject with tcp reset
                meta protocol ip6 iifname "eth2" tcp flags != syn / fin,syn,rst,ack ct state new counter accept
                ct state invalid counter drop
                ct state established,related counter accept
                ip6 nexthdr ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply } counter accept
                iifname "eth2" oifname "ip6tnl1" counter accept
                counter drop
        }
        chain output {
                type filter hook output priority filter; policy accept;
        }

参考にしたWebサイト