Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

104 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenWrt マルチセッション (ニチバンベンチ)対策 ホットプラグ適用方式

This README is edited in Japanese.

ニチバン対策(ホットプラグ適用方式)

ニチバンベンチについて

ニチバンベンチとは

ニチバン株式会社の公式サイトは、大量の小さなファイルを同時セッションで配信する構造のため、OpenWrtのMAP-E環境でNATポート枯渇を引き起こしやすい このサイトを10窓程度でリロードすることでポート枯渇問題を簡易的に再現できることから、「ニチバンベンチ」と呼ばれるようになった

ニチバン(SNATポート枯渇)対策とは

OpenWrtのmap.shが複数セッション確立時にSNATテーブルの競合によってポート枯渇エラー(SNAT failed)を引き起こす問題に対処することをいう

※ニチバン株式会社及びそのサービスに問題があるわけではなく、OpenWrtのMAP-E環境側の制約による現象です


map.shについて

mapパッケージに付属してる MAP-E方式をサポートするネットワーク設定スクリプト 19.07まではFW3対応、21.02以降FW4対応となった

sha1sum map-19.07.sh

  • 431ad78fc976b70c53cdc5adc4e09b3eb91fd97f map-19.07.sh

sha1sum map-21.02.sh ~ map-25.12.sh

  • 7f0682eeaf2dd7e048ff1ad1dbcc5b913ceb8de4 map-21.02.sh ~ map-25.12.sh
wget -qO- https://raw.githubusercontent.com/openwrt/openwrt/openwrt-**.**/package/network/ipv6/map/files/map.sh | sha1sum

問題点

MAP-Eはfw4/nftablesと互換性がありません #11972

MAP-T が割り当てられたポート範囲全体を活用しない #14449


ホットプラグ適用方式

attendedsysupgrade (owut)でmap.shが上書きされる為、ホットプラグ適用方式に変更

ファイル役割タイミング
/etc/init.d/mape-patchmap.sh portsetループ無効化START=19(netifd起動前)
/etc/hotplug.d/iface/99-mape-snatnumgen SNAT + DSCPリセット + conntrackmape ifup後

起動シーケンス

ブート
└─ START=19 mape-patch start()
├─ map.sh portsetループを無効化(sed)
└─ 99-mape-snat を生成
└─ netifd が mape ifup
└─ 99-mape-snat 発火
├─ numgen SNAT構築(全ポート均等分散)
├─ DSCPリセット(CS0)
└─ conntrackタイムアウト短縮

有効・無効の切り替え

/etc/init.d/mape-patch start # 有効化(map.sh sed + hotplug生成)
/etc/init.d/mape-patch stop # 無効化(hotplug削除 + nftクリーン + map.sh復元 + conntrackリストア)
対策内容

numgen SNAT(ポート均等分散)

numgen inc modで全割り当てポートを均等ラウンドロビン デフォルトmap.shは先頭16ポートに偏るが、本対策で240または1008ポート全てを均等に使用

DSCPリセット(NTT QoS帯域制限回避)

IPv4/IPv6両方のDSCPをCS0にリセット

conntrackタイムアウトチューニング

MAP-Eのポート数制約に合わせてconntrackの保持時間を短縮(ポート枯渇対策)

Block-QUIC-MAP-E

MAP-E環境ではQUIC(UDP/443)が大量のポートを消費するため、 QUICをブロックしてTCP/443にフォールバックさせることでポート枯渇を抑制

FMR(網内折り返し:Forwarding Mapping Rule: RFC 7597) UCI rulemap.sh.all のみ対応

network.*.fmr_ruleからFMRルール読み込み ※ホットプラグ方式は非対応


設定
#!/bin/sh
(
{ command -v apk > /dev/null 2>&1&& apk info -e map > /dev/null 2>&1; } || \
{ command -v opkg > /dev/null 2>&1&& opkg list-installed map | grep -q .; } || \
{ echo"先にMAP-E設定スクリプトを実行してください";exit 1; }
cat > /etc/init.d/mape-patch << 'INITD'#!/bin/sh /etc/rc.commonSTART=19STOP=19start() { sed -i '/for portset in.*PORTSETS/c\ for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null uci batch <<EOFset firewall.block_quic_mape=ruleset firewall.block_quic_mape.name='Block-QUIC-MAP-E'set firewall.block_quic_mape.proto='udp'set firewall.block_quic_mape.dest_port='443'set firewall.block_quic_mape.src='lan'set firewall.block_quic_mape.dest='wan'set firewall.block_quic_mape.target='DROP'set firewall.block_quic_mape.family='ipv4'set firewall.block_quic_mape.enabled='1'commit firewallEOF mkdir -p /etc/hotplug.d/iface printf '%s\n' \ '#!/bin/sh' \ '[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "mape" ] && {' \ ' R="/tmp/map-mape.rules"; [ -f "$R" ] || exit 0' \ ' eval $(cat "$R"); k=$RULE_BMR; [ -z "$k" ] && exit 0' \ ' n=0; P=""' \ ' for p in $(eval "echo \$RULE_${k}_PORTSETS"); do' \ ' s=${p%%-*}; e=${p##*-}; x=$s' \ ' while [ $x -le $e ]; do P="$P $n : $x ,"; n=$((n+1)); x=$((x+1)); done' \ ' done' \ ' [ "$n" -eq 0 ] && exit 0; P=${P%?}' \ ' nft delete table inet mape 2>/dev/null' \ ' nft add table inet mape' \ ' nft add chain inet mape srcnat { type nat hook postrouting priority 0\; policy accept\; }' \ ' V=$(eval "echo \$RULE_${k}_IPV4ADDR")' \ ' for t in icmp tcp udp; do' \ ' nft add rule inet mape srcnat ip protocol $t oifname "map-mape" counter snat ip to $V : numgen inc mod $n map {$P }' \ ' done' \ ' nft delete table inet mape_dscp 2>/dev/null' \ ' nft add table inet mape_dscp' \ ' nft add chain inet mape_dscp postrouting { type filter hook postrouting priority mangle\; policy accept\; }' \ ' nft add rule inet mape_dscp postrouting ip dscp set cs0 comment \"mape-dscp-reset-v4\"' \ ' nft add rule inet mape_dscp postrouting ip6 dscp set cs0 comment \"mape-dscp-reset-v6\"' \ ' sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600 \' \ ' net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout=120 \' \ ' net.netfilter.nf_conntrack_udp_timeout_stream=120 \' \ ' net.netfilter.nf_conntrack_icmp_timeout=60 \' \ ' net.netfilter.nf_conntrack_generic_timeout=60 >/dev/null 2>&1' \ '}' \ '[ "$ACTION" = "ifdown" ] && [ "$INTERFACE" = "mape" ] && {' \ ' nft delete table inet mape 2>/dev/null' \ ' nft delete table inet mape_dscp 2>/dev/null' \ '}' \ > /etc/hotplug.d/iface/99-mape-snat chmod +x /etc/hotplug.d/iface/99-mape-snat}stop() { rm -f /etc/hotplug.d/iface/99-mape-snat nft delete table inet mape 2>/dev/null nft delete table inet mape_dscp 2>/dev/null cp /rom/lib/netifd/proto/map.sh /lib/netifd/proto/map.sh 2>/dev/null sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 \ net.netfilter.nf_conntrack_tcp_timeout_time_wait=120 \ net.netfilter.nf_conntrack_udp_timeout=30 \ net.netfilter.nf_conntrack_udp_timeout_stream=180 \ net.netfilter.nf_conntrack_icmp_timeout=30 \ net.netfilter.nf_conntrack_generic_timeout=600 >/dev/null 2>&1 uci -q delete firewall.block_quic_mape uci commit firewall for s in network firewall dnsmasq odhcpd; do /etc/init.d/$s restart 2>/dev/null; done until ip addr show br-lan 2>/dev/null | grep -q 'inet '; do sleep 1; done /etc/init.d/ttyd restart 2>/dev/null}INITD
chmod +x /etc/init.d/mape-patch
/etc/init.d/mape-patch enable
/etc/init.d/mape-patch start
echo -e '/etc/hotplug.d/iface/99-mape-snat\n/etc/init.d/mape-patch'>> /etc/sysupgrade.conf
echo"設定が完了しました"echo"Enterキーを押すとサービスを再起動します"read dummy </dev/tty
forsin network firewall dnsmasq odhcpd;do /etc/init.d/$s restart 2>/dev/null;doneuntil ip addr show br-lan 2>/dev/null | grep -q 'inet ';do sleep 1;done
/etc/init.d/ttyd restart 2>/dev/null
echo"完了しました"
)

完全削除
/etc/init.d/mape-patch stop
/etc/init.d/mape-patch disable
rm -f /etc/init.d/mape-patch

正常性確認
#!/bin/sh
(
DONE=0; NONE=0
done_() { echo"設定済み: $1"; DONE=$((DONE+1)); }
none_() { echo"未設定 : $1"; NONE=$((NONE+1)); }
grep -q 'for portset in _; do continue' /lib/netifd/proto/map.sh 2>/dev/null && done_ "map.sh パッチ適用済み"|| none_ "map.sh 未適用"
[ -x /etc/init.d/mape-patch ] && done_ "init.d/mape-patch 配置済み"|| none_ "init.d/mape-patch 未配置"
[ -x /etc/hotplug.d/iface/99-mape-snat ] && done_ "hotplug/99-mape-snat 配置済み"|| none_ "hotplug/99-mape-snat 未配置"
NUMGEN=$(nft list table inet mape 2>/dev/null | grep -c numgen)
[ "$NUMGEN"-ge 3 ] && done_ "numgen SNAT ${NUMGEN}本 検出"|| none_ "numgen SNAT ${NUMGEN}本 未検出 (期待: 3)"
nft list table inet mape_dscp >/dev/null 2>&1&& done_ "mape_dscp 検出"|| none_ "mape_dscp 未検出"forpin \
"net.netfilter.nf_conntrack_tcp_timeout_established 3600" \
"net.netfilter.nf_conntrack_udp_timeout 120" \
"net.netfilter.nf_conntrack_udp_timeout_stream 120";do
k=${p%*}; v=${p##* }; val=$(sysctl -n $k2>/dev/null)
[ "$val"="$v" ] && done_ "$k=$val"|| none_ "$k=$val (期待: $v)"done
FW4=$(fw4 restart 2>&1| grep -c "ignoring section")
[ "$FW4"-eq 0 ] && done_ "fw4 クリーン"|| none_ "fw4 portsetルール ${FW4}件残存"
ping -c 2 -W 3 1.1.1.1 >/dev/null 2>&1&& done_ "IPv4疎通あり"|| none_ "IPv4疎通なし"echo"========================"echo"設定済み: $DONE / 未設定: $NONE"
)

ブラウザ動作確認

ベンチマーク該当サイトの構造

偶然にも同じホスティングサービス:KDDI Web Communications Inc.(KWC-NET: 150.60.0.0/16) CDNなし:オリジンサーバー(Cloudflare等を挟まない)から直接配信 HTTP/1.1スタイル:多数の並列TCPコネクションを開く

対策の有効・無効化切り替え

コマンド:

/etc/init.d/mape-patch start # 有効化
/etc/init.d/mape-patch stop # 無効化

LuCI:

システム > 起動 > mape-patch開始停止ボタン

ベンチマーク:

切り替え後、10窓程度リロードして挙動を比較 ※3窓以上で体感出来るはず


過去版(map.sh差し替え方式)

Qiita


First draft: 5 Aug 2023

Update: 10 April 2026

Releases

Packages

Contributors

Languages