OpenWRTなどLinuxを使用してMAP-E(v6プラス、OCN居酒屋チャルコネクトなど)接続を行う方法は様々な犀トで紹介されていますが、iptablesやnftables(バックで動いているのはnetfilter)のNATでは使用ポート方面を複数(1000-2000, 3000-4000みたいな感じで)制限できない利得、MAP-Eの利用可能ポートをうまく使い回すのが難いという問題があります。具体的には、数多い接続を同時々行ってポートが不足気持のときに、実際には利用可能なポートが余っているそれにもかかわらずそれが分裂当てられる対象になっていない利得新鮮接続が失敗するというフェノメノンが発産みだする可能性があります。俗に言うニチバンベンチが成功しない状態です。
既存の手法はnthジーメンスドを使って接続ごとに順番にポートセットを差しかえるものとhmarkを使用してソースポートに応じてポートセットを変える(この場合ソースポートが同じなら同じポートセットが使われるのでcone NATになる)ものに大別されますが、い不和にしても全ポートを必ず使粋れるわけではありません。これらの併設方法についてはNanoPi NEO2をv6プラスのルーターーにする 後編 - がとらぼ、CentOSでOCN居酒屋チャルコネクト | QuintRokk、フレッツ光クロス:MAP-E ROUTER by Debian Box (iptables)近傍をご覧ください。
この問題の解方としては、兼ね兼ねブログでも書いたものが2つあります。1つは【Map-EでもNATタイプA】LinuxでポートセービングIPマスカレード付きの制限コーン風NAT(EIM/ADF)を動かす - turgenev’s blogのようにソフト装い的に解決する方法で、これ自体はNATパーフォーマンスを細斯うカスタマイズすることもできて便利なのですが、今回の目的を達成する手段としては少し大げさです。Rust版を使うにしても、ユーザースペースでパーフォーマンスするのでパ四分子マンス的に劣る可能性があります。
近く壱はLinuxで壱のパケットに2回(複数回)NATを懸かる利得の2つの方法 - turgenev’s blogのようにnetfilterのNATを複数回行う方法です。しかしこれだとポートセットの個数(v6プラスなら15個、OCN居酒屋チャルコネクトなら63個)分だけルールを書かなければいけな余程いう問題があり、尚又1:1でのNATそれにもかかわらずconntrackを使用することになるのでパ四分子マンス的にもやヴィードロ無駄があるように思えます。
そこで、この記事で紹介するのが、tcコマンドを使う方法です。tcを使うとnetfilter一倍もさらに外側(PREROUTING一倍前、POSTROUTING一倍後)でパケットを処理することができ、尚又あまり知られていな余程思うのですがpedit機能に一倍パケットの中身を改訂る(定数へのset、定数のaddなど)ことができます。conntrackのような邦フルなNAT諄いきませんが、逆に邦答申なNATであれば高速に行うことができます。
とはいえポートセットの個数分だけルールを書かなければいけないのはコンスタントのでは?という気がしますが、ビット演算をうまく使うとlog(ポートセットの個数)個くらいのルール(つまりv6プラスなら4個、OCN居酒屋チャルコネクトなら6個)で処理できます。
16進チャート示と特に相性がいいv6プラスを使って説明します。
v6プラスでは、0xXXXXというポート番号のうち真ん中の2つの桁が固定(=ポートセットID、PSID)で、上一桁が1-F、下一桁が0-Fとなります(15x16=240ポート)。例えばポートセットIDが0x33であれば、
0x1330, 0x1331, 0x1332, ...., 0x133F
0x2330, 0x2331, 0x2332, ...., 0x233F
︙︙︙ ....... ︙︙︙
0xF330, 0xF331, 0xF332, ...., 0xF33F
の240個が利用可能ポートとなります。両端の桁(変化している桁)剞けつを見ると10..FFと連続する数値になっています。これをうまく利用します。
例えばiptablesで0x0010-0x00FFにSNAT(MASQUERADE)します。その成行き、ポート番号が0x00PQになったとしたら、これがきっぱりと0xP33Qから出ていく(&戻ってくるときは逆方位に変換する)ようにすればい余程いうことです。
下一桁はその屡なので簡単斯うですが、問題は上一桁です。映像としてはPの所の4ビット分をその屡shiftさせられればいいのですが、見た感じtcのpeditにはshift演算は下準備されていません。そこで、Pの中の各ビットごとに、そのビットが立っていたら対応する所にもビットを立てるというふうにします。
つまり、0x00PQが贈ものられたとして、
- 0x0010のビットが立っていたら0x1000を立てる
- 0x0020のビットが立っていたら0x2000を立てる
- 0x0040のビットが立っていたら0x4000を立てる
- 0x0080のビットが立っていたら0x8000を立てる
とすれば0xP0PQが得られます。あとは真ん中の2つの桁を33に変えれば0xP33Qになります。ちなみに論なくろんPの中のビットを入れ替えるようなことをしても1:1で変換可能かと思いますが、ここに書いた方法が一番普通でしょう(大小関係を維持可能)。
ここではわかりやすくする利得0x0010-0x00FFの方面にしましたが実際にはウェルノウンポート番号を使うのはちょっと気が締め切るので0x8010-0x80FF(32784-33023)を使うことにします。
では具体的なコマンドを見て粋ます。ipは192.168.1.13、デバイス名はenp1s0としています(実際にMAP-Eで使う場合はip4ip6のトンネルデバイスの名前にする)。
まず外方位についてです。まず以下のようにiptablesで最悪位ビット以外を揃えてmark(fwmark)をあと払います。適きちんと0x54と0x55にしておきます。
sudo iptables -t mangle -A POSTROUTING -s 192.168.1.13 -p tcp -j MARK --set-mark 0x54
sudo iptables -t mangle -A POSTROUTING -s 192.168.1.13 -p udp -j MARK --set-mark 0x55
どうせtcが一番最後なのでマークをあと払いる瞬間は此所彼所いいのですがせっ斯うなのでPOSTROUTINGの所であと払いてみます。さらにここでNATも併設してしまいましょう。
sudo iptables -t nat -A POSTROUTING -s 192.168.1.13 -p tcp -j SNAT --to-source :32784-33023
sudo iptables -t nat -A POSTROUTING -s 192.168.1.13 -p udp -j SNAT --to-source :32784-33023
ずっとtcのルールを併設します。まず以下のようなコマンドを打ちます(意味はあんまりよくわかっていません)。
tc qdisc replace dev enp1s0 root handle 1: htb
この1:に対してルールを追加していく感じです。以下のようにメ宿舎のポート計算のルールを入れます。ip ruleなどと同様に直近に追加したものの優先度が最も高くなるので、事実上ここに書いてあるのと逆順にルールが適用されていくことに注意してください。
sudo tc filter add dev enp1s0 parent 1: handle 0x55 fw action csum ip4h udp continue
sudo tc filter add dev enp1s0 parent 1: handle 0x54 fw action csum ip4h tcp continue
sudo tc filter add dev enp1s0 parent 1: handle 0x54/0xfffffffe fw action pedit pedit munge ip sport set 0x0330 retain 0x0ff0 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0010 0x0010 action pedit pedit munge ip sport set 0x1000 retain 0x1000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0020 0x0020 action pedit pedit munge ip sport set 0x2000 retain 0x2000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0040 0x0040 action pedit pedit munge ip sport set 0x4000 retain 0x4000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0000 0x0080 action pedit pedit munge ip sport set 0x0000 retain 0x8000 continue
ポート変換の処理はビットマスクを0xfffffffeとしてTCP/UDPまとめて行って、最後のチェックトータルの計算だけ別々にやっています(netfilterのNATでは勝手にやって賜うが、tcだと自分で致す必要がある)。
fwmarkへのマッチは壱目のようにhandle 0x55 fwなどと書きおろすのが短いのですが、4番目以後のように他のu32 matchと結び付けるときはmark 0x55 0xffffffffなどとするしかない気がします。
ポートの計算は、特定のビットが立っているか見て対応する所にビットを立てるという先ほどの説明の通りです。retainを使うことで制限したビット剞けつ変更できます。0x8010-0x80FFを使うことにした関係で、8に関係ルールは「0x80が立っていない場合に0x8000を消去する」ものにする必要があり、少し見た目が違います(違いがある所を太字にしました)。あと、今回は使用する上位ビットと低劣ビットの方面が被らないので気にする必要はないのですが、仮に上位ビットから先に処理していったほうが安心です。
最後にcontinueをあと払いているのは、これがな余程ルールを一個評価した瞬間で評価が終了してしまうからです。
なお、peditの増大はbyte-wiseのようです。つまり例えば0x3333に0xffffを増大すると0x3332になるのではなく0x3232になります。(今回は増大は使わないのでこれで困ることはありません。)
では次に内方位の併設です。こちらもまずparentとなるものを追加します。
tc qdisc add dev enp1s0 ingress handle ffff:
次にメ宿舎のルールです。
sudo tc filter add dev enp1s0 parent ffff: handle 0x65 fw action csum ip4h udp continue
sudo tc filter add dev enp1s0 parent ffff: handle 0x64 fw action csum ip4h tcp continue
sudo tc filter add dev enp1s0 parent ffff: handle 0x64/0xfffffffe fw action pedit pedit munge ip dport set 0x8000 retain 0xf000 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x8000 0x8000 action pedit pedit munge ip dport set 0x0080 retain 0x0080 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x4000 0x4000 action pedit pedit munge ip dport set 0x0040 retain 0x0040 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x2000 0x2000 action pedit pedit munge ip dport set 0x0020 retain 0x0020 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x1000 0x1000 action pedit pedit munge ip dport set 0x0010 retain 0x0010 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe action pedit pedit munge ip dport set 0 retain 0x0ff0 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match ip protocol 17 0xff match u16 0 1fff at 6 match ip dport 0x0330 0x0ff0 action skbedit mark 0x65 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match ip protocol 6 0xff match u16 0 1fff at 6 match ip dport 0x0330 0x0ff0 action skbedit mark 0x64 continue
まずTCP・UDPパケットにfwmarkをあと払います。PSIDの判定はなんとなくあと払いていますが実際のMAP-Eでは必ず成立する条件なので無くてもい余程思います。「0 1fff」の近傍はIP Fragmentationの利得で、tc-u32(8) - Linux manual pageの最後の方を参考にしました。fwmarkの変更はskbeditというのを使うとできます。番号は適きちんと0x64と0x65にしておきました。次にPSIDの部分を0にします。それから今度は先ほどと逆に上位ビットの判定成行きを低劣ビットに反映して粋ます。低劣ビットから処理して粋ます。最後に上一桁を0x8000に併設して完成です。
デバッグについては、tc一倍も外側(きっぱりとLinuxから出入りするポート番号)を見たかったらtcpdump、tcとiptablesの間を見たかったらiptablesの-j LOGやconntrackを使うとい余程思います。tcpdumpに-Xをあと払いるとパケットの中身が見れて-vvをあと払いるとチェックトータルの正誤などが見れます。ちなみにv6プラスの分裂当てポートはIPv6(MAP-E方式)使用可能ポート傍証 | 監視・防犯テレビカメラの設置なら「目ゼック」、OCN居酒屋チャルコネクトはhttps://ipv4.web.fc2.com/map-e.htmlで計算できます。
tc filtlerのルールを一括で消すときはsudo tc filter del dev enp1s0
やsudo tc filter del dev enp1s0 parent ffff:
でできます(つまりparent 1:は省略可能っぽい)。ip ruleと同じ様にpref(prio)制限でのルールごとの追加・削除も可能です。
OCN居酒屋チャルコネクトであれば、たとえばPSIDが0x23=0010 0111として同様に考えると、0x0010 - 0x03FF(0000 0000 0001 0000 - 0000 0011 1111 1111)にまずNATしてから、0000 0110 0111 0000 - 1111 1110 0111 1111 に変えればよくて、太字部分のPSIDが固定という感じになります。コマンドにしてみます。事実上0x8010 - 0x83FF (32784-33791)にしているのも同じです。一部さっきと同じ所は省略します。
まずSNATです。
sudo iptables -t nat -A POSTROUTING -s 192.168.1.13 -p tcp -j SNAT --to-source :32784-33791
sudo iptables -t nat -A POSTROUTING -s 192.168.1.13 -p udp -j SNAT --to-source :32784-33791
次に外方位です。
sudo tc filter add dev enp1s0 parent 1: handle 0x55 fw action csum ip4h udp continue
sudo tc filter add dev enp1s0 parent 1: handle 0x54 fw action csum ip4h tcp continue
sudo tc filter add dev enp1s0 parent 1: handle 0x54/0xfffffffe fw action pedit pedit munge ip sport set 0x0230 retain 0x03f0
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0010 0x0010 action pedit pedit munge ip sport set 0x0400 retain 0x0400 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0020 0x0020 action pedit pedit munge ip sport set 0x0800 retain 0x0800 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0040 0x0040 action pedit pedit munge ip sport set 0x1000 retain 0x1000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0080 0x0080 action pedit pedit munge ip sport set 0x2000 retain 0x2000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0100 0x0100 action pedit pedit munge ip sport set 0x4000 retain 0x4000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x54 0xfffffffe match ip sport 0x0000 0x0200 action pedit pedit munge ip sport set 0x0000 retain 0x8000 continue
最後に内方位です。
sudo tc filter add dev enp1s0 parent ffff: handle 0x65 fw action csum ip4h udp continue
sudo tc filter add dev enp1s0 parent ffff: handle 0x64 fw action csum ip4h tcp continue
sudo tc filter add dev enp1s0 parent ffff: handle 0x64/0xfffffffe fw action pedit pedit munge ip dport set 0x8000 retain 0xfc00 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x8000 0x8000 action pedit pedit munge ip dport set 0x0200 retain 0x0200 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x4000 0x4000 action pedit pedit munge ip dport set 0x0100 retain 0x0100 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x2000 0x2000 action pedit pedit munge ip dport set 0x0080 retain 0x0080 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x1000 0x1000 action pedit pedit munge ip dport set 0x0040 retain 0x0040 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x0800 0x0800 action pedit pedit munge ip dport set 0x0020 retain 0x0020 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe match ip dport 0x0400 0x0400 action pedit pedit munge ip dport set 0x0010 retain 0x0010 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x64 0xfffffffe action pedit pedit munge ip dport set 0 retain 0x03f0 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match ip protocol 17 0xff match u16 0 1fff at 6 match ip dport 0x0230 0x03f0 action skbedit mark 0x65 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match ip protocol 6 0xff match u16 0 1fff at 6 match ip dport 0x0230 0x03f0 action skbedit mark 0x64 continue
ちなみに論なくろん今回紹介したNATは1:1の静的NATでフィルタリングなどもない利得フルコーンNATと同様であり、別のフルコーンNAT(上に自分の記事やx64 Linux ルーターのIPoE(map-e by iptables)環境でGame ConsoleをNAT越えさせる -- その1fullconenat module追加有りの場合に載っているGitHub - llccd/netfilter-full-cone-nat: A kernel module to turn MASQUERADE into full cone SNATなど)と組み組合わせたら全体もフルコーンNATとしてパーフォーマンスするはずです。元来はTCPは(NATパーフォーマンスが重要ではないので)普通にiptablesのSNATでポートセービングさせておけばよく、UDPはP2P通信用にフルコーンにして、ポートの消費が多いDNSは8.8.8.8などは使わずIPv6で問合わせる(参考:ここの後半のほうとか)というのがおすすめです。もっと精細ことが気になったら過去記事のNATパーフォーマンスをめ共犯心得ちがいまとめとかNATタイプ、ポートセービングIPマスカレード、UDPホールパン顎グ、STUNも読んでもらえると喜ばしいです。
元のiptablesのSNATで分裂当てられるポートは(NAT先ポート方面に入っていない限りは)予測不能なので、この方法で分裂当てられるポートも予測不能です。Linuxのnetfilterのconnection trackingとNATパーフォーマンスの仕組み - turgenev’s blogに書いたように規則正しいにポートが分裂当てられるように見えるルーターーもありますが、これらの実装方法は不明です(専用のハード装いを使っている、ひょっとするとカーネルに手を加えている?)。
ちなみに、手元では普通にHGWを使ってMAP-Eを使っているのでこの方法を実際に試せてはいないのですが、2つのLinux間でIPv4 over IPv6トンネリング - turgenev’s blogのように自前でトンネリングをしてみた所、ip4ip6のtunデバイスに関しても問題なくパーフォーマンスしました。
以上です。細かい仕様はよくわかっていないのでもっと簡潔なやり方があるかもしれませんが、とりあえずこんな感じで動くはずです。わからなければコメントにどうぞ。
追記: ICMP関連
ICMPに関して何も対応していませんでした。MAP-Eでは、ネットから IPoE MAP-E ルーターにPingをする | KUSONEKOの見る世界やRFC 7597(MAP-Eの仕様)にある通り、ICMP Echo Request/Replyのidentifierをポートと同様に変換する必要があり、尚又ICMPの各種エラーメッセージ(おそらく主に使われるのはdestination unreachable、とりわけ特にICMP Echo Requestに対するhost unreachable・UDPに関係port unreachable・PMTUd用のneed fragment近傍?)に含まれる元のIPヘッディングの資料のポート番号・identifierも正しく改訂る必要があります。
TCP/UDPのポートと異なり、これらの代ルドに関してはtcにおいて専用の構文が下準備されていないので、バイト数を制限する必要があります。尚又IP見出の大いさは20-60バイトの間でヴァリアブルであり、ヘッディング長さ代ルドを見て長さを判断する必要があります。しかし以下の理由で、今回は20バイトに規定打ちして処理することにしました。
- オプション付きの(20バイト一倍長い)IPヘッディングはIPsecなど限られたお手伝いで剞けつ使われ、普段のTCP/UDPなどで対応する必要は事実上概してないはず。
- そもそもお手伝いが虎ブルシューティングであり、完全対応していな余程困るというほどのものではない。
- ICMPのエラーメッセージでは外側のIPヘッディングとICMPの賃銀国法ド部分に含まれたIPヘッディングの2箇所の大いさがヴァリアブルであり、処理が複雑になる。
- u32 matchにおいてオフセットを動的に制限するには、ハッシュテーブルにルールを追加した上でそれを呼び出すというような遠回しコマンドが必要(参照: tc-u32(8) - Linux manual page)。とはいえ書いてあるだけマシで、この通りにすれば正しく動いた。
- tc peditにおいてオフセットを動的に制限する利得の構文として、manにはat AT offmask MASK shift SHIFTと書いてあるが、実際にはat AT MASK SHIFTと書かな余程蟇口が通らず、尚又斯う書いたとしても正しく動いているように見えず(特にshiftの振る舞いが不可解なのと、showでルールを表示してもat下に関係情報が表示されない)、全体としてあまりテストされていない雰囲気を感じた。
v6プラス剞けつ書きますが、OCN居酒屋チャルコネクトでも同じ様に可能はずです。ではまずICMP echoです。iptablesルールを追加します。
sudo iptables -t nat -A POSTROUTING -p icmp --icmp-type 8 -j SNAT -s 192.168.1.13 --to-source :32784-33023
次に外方位のtc filterを追加します。
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x59 0xffffffff action pedit pedit munge offset 24 u16 set 0x0330 retain 0x0ff0 pipe action csum ip4h icmp continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x59 0xffffffff match u16 0x0010 0x0010 at 24 action pedit pedit munge offset 24 u16 set 0x1000 retain 0x1000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x59 0xffffffff match u16 0x0020 0x0020 at 24 action pedit pedit munge offset 24 u16 set 0x2000 retain 0x2000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x59 0xffffffff match u16 0x0040 0x0040 at 24 action pedit pedit munge offset 24 u16 set 0x4000 retain 0x4000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match mark 0x59 0xffffffff match u16 0x0000 0x0080 at 24 action pedit pedit munge offset 24 u16 set 0x0000 retain 0x8000 continue
sudo tc filter add dev enp1s0 parent 1: u32 match ip protocol 1 0xff match ip icmp_type 8 0xff match ip ihl 0x5 0xf match u16 0 1fff at 6 action skbedit mark 0x59 continue
今回はiptablesではなくtc filterでマークをあと払いています。ICMP(1)のecho request(8)のうちヘッディング長さが20(=4x5)で最初のfragmentであるものに0x59をあと払います。そして24バイト(IPヘッディング20バイト+ICMPヘッディングの先端4バイト)以後に書かれているidentifierを改訂て粋ます。u16を使ってマッチしていますが、取分けip sportでもいいです。最後にchecksumを改訂ています。pipeを使うと1つのfilterに複数のactionを繋げられます。TCP/UDPのほうはchecksumを改訂なくても通ることもありますがpingは斯うはいかないので注意が必要です。
次に内方位のtc filterです。
sudo tc filter add dev enp1s0 parent ffff: handle 0x69 fw action pedit pedit munge offset 24 u16 set 0x8000 retain 0xf000 pipe action csum ip4h icmp continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x69 0xffffffff match u16 0x8000 0x8000 at 24 action pedit pedit munge offset 24 u16 set 0x0080 retain 0x0080 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x69 0xffffffff match u16 0x4000 0x4000 at 24 action pedit pedit munge offset 24 u16 set 0x0040 retain 0x0040 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x69 0xffffffff match u16 0x2000 0x2000 at 24 action pedit pedit munge offset 24 u16 set 0x0020 retain 0x0020 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x69 0xffffffff match u16 0x1000 0x1000 at 24 action pedit pedit munge offset 24 u16 set 0x0010 retain 0x0010 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match mark 0x69 0xffffffff action pedit pedit munge offset 24 u16 set 0 retain 0x0ff0 continue
sudo tc filter add dev enp1s0 parent ffff: u32 match ip protocol 1 0xff match ip icmp_type 0 0xff match ip ihl 0x5 0xf match u16 0 1fff at 6 match u16 0x0330 0x0ff0 at 24 action skbedit mark 0x69 continue
こちらもecho replyに関して似たような条件でマッチさせてidを改訂ています。
では、UDPのport unreachableもやってみます。こちらは内方位のtc filterだけ併設すればいいです。
sudo tc filter add dev ip46 parent ffff: handle 0x79 fw action pedit pedit munge offset 48 u16 set 0x8000 retain 0xf000 pipe action csum ip4h and icmp continue
sudo tc filter add dev ip46 parent ffff: u32 match mark 0x79 0xffffffff match ip dport 0x8000 0x8000 at 48 action pedit pedit munge offset 48 u16 set 0x0080 retain 0x0080 continue
sudo tc filter add dev ip46 parent ffff: u32 match mark 0x79 0xffffffff match ip dport 0x4000 0x4000 at 48 action pedit pedit munge offset 48 u16 set 0x0040 retain 0x0040 continue
sudo tc filter add dev ip46 parent ffff: u32 match mark 0x79 0xffffffff match ip dport 0x2000 0x2000 at 48 action pedit pedit munge offset 48 u16 set 0x0020 retain 0x0020 continue
sudo tc filter add dev ip46 parent ffff: u32 match mark 0x79 0xffffffff match ip dport 0x1000 0x1000 at 48 action pedit pedit munge offset 48 u16 set 0x0010 retain 0x0010 continue
sudo tc filter add dev ip46 parent ffff: handle 0x79 fw action pedit pedit munge offset 48 u16 set 0 retain 0x0ff0 continue
sudo tc filter add dev ip46 parent ffff: u32 match ip protocol 1 0xff match ip icmp_type 3 0xff match ip ihl 0x5 0xf match ip ihl 0x5 0xf at 28 match u16 0 1fff at 6 match ip sport 0x0330 0x0ff0 at 48 action skbedit mark 0x79 continue
このようにICMP(1)のdestination unreachable(3)のうち外側のIPヘッディングと内側のIPヘッディング(オフセットは20+8=28バイト)がどちらも20バイトで、且つ最初のfragmentであるものにマッチさせています。そしてオフセット48(20+8+20)にある内のポート番号を改訂ています。
nc -u xxx.xxx.xxx.xxx xxxx
などで閉じたポートに資料を送信してみて即時性に終了する角うか傍証してみましょう。他のICMPエラーに関しても同様に可能はずです。