nftables で設定したマークは、あるクラスの通信をトンネル経由で送るはずだが、通信は物理インターフェースから出続けている。ルールセットは単体では正しく見える。たいてい起きているのは、別の何かが同じマークを書き込んだか、ルーティングルールが新しい値ではもう満たさないマスクに一致しているかだ。

確認 1:カーネルが一致させるルールを読む

ファイアウォールではなく、ポリシールールから始める。カーネルは優先度順にルールを参照し、最初に一致したものがテーブルを決める。fwmark の値とマスクをルール自体から読み、設定したマークを持つパケットが実際にそれに一致するか確認する。完全な 32 ビット値を期待するルールは、単一ビットしか持たないパケットには一致しない。

次に、そのルールがどのテーブルを選ぶのか、テーブルに何が入っているのかを確認する。空のテーブルを指すルールは検索失敗を生み、多くのシステムではパケットが次のルールに落ちて物理インターフェースから出ることを意味する。

これを決めるコマンドは二つある:ルール一覧と、そのテーブルにおける宛先のルート検索だ。

確認 2:パケットマークか接続マークか

nftables はパケットにマークを設定でき、接続追跡エントリにもマークを設定できるが、これらは別のオブジェクトだ。パケットマークはパケットの処理パスの間だけ生き、接続マークはフローの間持続し、意図的にコピーする必要がある。片方を設定してもう片方を期待するのはよくある半分の修正だ。

ルーティングルールがパケットマークを参照しているなら、経路の後半で接続マークを復元してもその判定には何の影響もない、判定はすでに済んでいるからだ。

どちらのオブジェクトがルーティング信号を持つのかを決め、ルーティング判定が行われる地点でそれを設定する。

確認 3:そのマークを他に誰が書くか

WireGuard 自身のツールは自分の UDP パケットにマークを付け、暗号化されたトランスポートが属するトンネルへ戻されないようにする。ルールセットが同じフィールドに無関係な値を割り当てると、その除外が消え、トンネルの外側の通信が自分自身に入ろうとする。症状は、確立した後に通信を通さなくなるトンネルだ。

マークは他のファイアウォールツール、コンテナネットワーク、別の目的のポリシールーティングでも使われるため、二つのサブシステムがフィールド全体を書くのは、それぞれ単体で正しくても衝突になる。

もう一つ追加する前に、書き込み元を一覧にする。

確認 4:マスクが残すものを決める

マークは 32 ビットのフィールドで、マスクは複数の所有者がそれを共有する方法だ。マスク付きで値を設定すると自分のものでないビットは保持され、フィールド全体を設定すると他の全員のビットが壊れる。フィールドに触れるすべてのルールでマスクを読み、自分が書いていないものも含める。

ビットの割り当てをルールセットのそばに記録する。

さもなければ次の人がすでに所有者のいる値を再利用し、ここでの衝突はアドレス指定の不具合なのにルーティングの不具合に見える。パケットメタデータに関する nftables のドキュメントがマーク操作自体を説明している。

確認 5:検索ではなく実フローで検証する

単純なルート検索はマークを持たないため、実際のパケットとは違う経路を示すことがある。気にしている通信を生成し、カウンタ、接続追跡エントリ、実際に出ていくインターフェースを観察する。広すぎるマークは除外を壊すのに成功したように見えるため、含めるクラスと除外するクラスの両方をテストする。含めるクラスが動き、除外するクラスもトンネルに入るなら、マークは狭すぎるのではなく広すぎる。

新規と既存のフローが正当に別の経路を取ることがあるため、稼働中のセッションで更新を強制するために接続状態をフラッシュするのは避ける。

マーク、マスク、テーブルを一か所に記録すれば、次の編集はレビュー可能になる。

関連記事