旧回線から新しく光クロス回線を引きました。enひかりです。光クロスの環境では、PPPoEを提供しているプロバイダーは極端に少なく、IPv6 IPoEでのインターネット接続しか選択肢がありません。今使っているプロバイダーでは、v6プラス(MAP-E)、Xpass(DS-Lite)が選択式になっていて、以前にDS−Liteを試用したので、今回はMAP-Eにしました。それと、今回は固定IP(IPIP6)も使えるようにしました。というわけで、環境が出来たので、OpenWrtで接続をした記録です。
OpenWrtの設定
wan6の設定
25.12ではONU直結からDHCPv6でアドレスを取得する場合、クライアントID設定が必要になりました。
設定しないとONUが応答しないので、IPv6のアドレスが降ってきません。
設定方法は次の通りです。

wan6にIPv6アドレスを割り当てるには、詳細設定の下の方にある、「IPv6割り当て長」を64にしますが、PDを受けて配布するルーターとして使うだけならここの設定は不要みたいです。

IPv6で設定していく
v6プラス、固定IPサービスのどちらもトンネルでIPv4通信をします。
wanにケーブル繋いでIPv6のアドレスを取得したら、必要なパッケージを追加していきます。
必要パッケージは map ip-full luci-proto-ipv6
素のOpenWrtではMAP-Eが使えないので次のソフトウェアインストールします。インストールはluciからやるか、コンソールでopkg installかapk add。
#snapshotだとapk
apk add map ip-full luci-proto-ipv6
luciからだと一つずつインストールで。めんどい
MAP-Eの設定
必須パッケージを入れて再起動をしたら、例のページからMAP-Eの設定情報を計算します。本来は設定サーバーから取ってくるらしいですが、認証が必要らしいので、手動計算します。
例のページ https://ip4.web.fc2.com/map-e.html
そのままoption出ているので、sshからconfig貼り付けるか、Webから入力します。
今回はwanをそのまま編集して設定変えました。
luciからだと [従来のMAPを使用] のチェックいれるのを忘れずに
全部設定が完了すると、トンネル用の_がついたインターフェース増えてIPv4接続されます。
なんか面白い資料見つけたので今度追記か実験します。読みたい人向けにこんな資料も 徹底解説 v6プラス
ページが見られなくなる問題
そのままではポートセットが一部しか使われず、ポートが枯渇してIPv4のページが開けなくなる問題が頻発するので、次のスクリプトをfirewall.userあたりに追加して実行されるようにします。が、めんどくさいのでスタートアップのローカルにべた貼りとかでもいいみたいです。
#!/bin/sh
# suggest https://aose.hatenablog.jp/entry/2022/12/04/224418
# this script based https://zenn.dev/wistnki
# use map version
#
IPv4=[IP Address]
TUNDEV=map-wan
PSID=36
PREFIX=4096
BLOCKS=15
nft table ip mape_nat
nft delete table ip mape_nat
nft add table ip mape_nat
nft add map ip mape_nat chain_map { type mark : verdict \; }
nft add chain ip mape_nat POSTROUTING { type nat hook postrouting priority 100 \; }
nft add rule mape_nat POSTROUTING oifname $TUNDEV meta l4proto { tcp, udp, icmp } mark set numgen inc mod $BLOCKS offset 0x11 counter
nft add rule mape_nat POSTROUTING oifname $TUNDEV meta mark vmap @chain_map;
for r in `seq 1 $BLOCKS ` ; do
mark=$(( r + 0x10 ))
port_l=$(( r * PREFIX + PSID * 16 ))
port_r=$((port_l + 15))
nft add chain ip mape_nat mape_ports$r
nft add rule ip mape_nat mape_ports$r meta l4proto { tcp, udp, icmp } counter snat to $IPv4:$port_l-$port_r persistent
nft add element ip mape_nat chain_map { $mark : goto mape_ports$r }
done
他の参考も置いておく

俗にニチバンベンチとか言われていましたが、IPv4でしか接続出来ないサイトに接続すると、ポート不足でページ開けなくなる問題を軽減してくれます。それでもF5しすぎると駄目なところは駄目っぽいので、固定IP使うしかなさそう。
固定IPサービス
固定IPサービスはipip6での単純トンネル。PPPoE接続の感覚で利用できます。
予算があったらぜひ使いたい固定IP。
なお、NTTのレンタルルーターなど業務機でしか対応機種が無いみたいで、一般のWiFiルーターは対応状況壊滅。
あとはOpenWrtなどソフトルーターが選択肢。
IPIP6の設定
難しいことはなく、プロバイダから届いた設定のとおりに入力していきますが、
たぶんプロバイダはPrefixを書いてこないので、察して降ってきたwan6のアドレスで入力していきます。
インターフェイスを新規作成でwan2を作って設定します
リモートIPv6アドレスにBRアドレス
IPv4アドレスは、割当られた固定IP
IPv6アドレスは、今降ってきているPrefix + プロバイダが通知してきたIPv6インターフェイスID(後半のアドレス)
MTUは1460
注意点として、hintに必ず0を入力します。
ファイアウォール設定はとりあえずWANと同じにしておきます。
アップデートURLも通知送られてきていると思いますが、IPv6の割当変更が起きたときに叩くみたいです。今回は特に何もしなくてもつながっているので未使用です。
23.05以前のOpenWrtの場合はパッケージ壊れているとか、そもそもIPIP6非対応なので、githubからスクリプト取ってきて置き換え必要です。
完
というわけでv6プラスに固定IPサービスしたら、実質2つIPが使えるようになりました。
ちょっと思っていたのと違うんですけど、mwan3で使い分けたりですかね。
最近の所感、IPv6でほとんどのサイトが繋がるようになってきていて、IPv4の必須性は薄れつつあるように思います。現状困るようにおもえるのは、githubとYahooくらいです。
HGWを契約していると/56でしかMAP-E張れないらしく、電話で設定を/60変更してもらう必要があるそうで注意が必要です。
mapcalc
もしかしたら一部の環境の人には使えるかもしれないので置いときます。
問題が起きないのでテストしかしていないですが、 /usr/sbin/mapcalc を置き換えて chmod +x で権限あげてください
OpenWRT mapcalc Issues on 64-bit Architecture / OpenWRTの64bitアーキテクチャで、mapcalcが適切に動かない話 [MAP-E]
#!/bin/sh
# mapcalc - MAP parameter calculation (pure shell implementation)
#
# Replaces the C binary. Avoids the 64-bit undefined behaviour in the
# original port-set computation (negative shift amount in
# `1 << (16 - offset - psidlen)`) by performing all integer arithmetic
# in the shell, which uses 64-bit signed integers on 64-bit targets.
#
# Derived from mapcalc.c:
# Author: Steven Barth <[email protected]>
# Copyright: (c) 2014-2015 cisco Systems, Inc.
# (c) 2015 Steven Barth <[email protected]>
# (c) 2018 Hans Dedecker <[email protected]>
# Shell port:
#
# SPDX-License-Identifier: GPL-2.0-only
LEGACY="${LEGACY:-0}"
[ $# -lt 2 ] && {
echo "Usage: $0 <interface|*> <rule1> [rule2] [...]" >&2
exit 1
}
MAIN_IFACE="$1"
shift
# ---------------------------------------------------------------------------
# IPv6 / IPv4 utilities
# IPv6 addresses are represented internally as 32-char lowercase hex strings,
# MSB first, no separators. e.g. "20010db8000000000000000000000001"
# ---------------------------------------------------------------------------
# Expand any valid IPv6 address to 32 lowercase hex chars.
# Uses awk with a pure-decimal hex converter (portable to busybox awk).
ipv6_to_hex() {
echo "$1" | awk '
function h2d(h, i,r,c,n,a) {
r = 0; n = split(toupper(h), a, "")
for (i = 1; i <= n; i++) {
c = index("0123456789ABCDEF", a[i]) - 1
r = r * 16 + c
}
return r
}
{
addr = $0
if (index(addr, "::") > 0) {
split(addr, h, "::")
nl = split(h[1], L, ":")
nr = split(h[2], R, ":")
miss = 8 - nl - nr; j = 0
for (i = 1; i <= nl; i++) g[++j] = L[i]
for (i = 1; i <= miss; i++) g[++j] = "0"
for (i = 1; i <= nr; i++) g[++j] = R[i]
} else {
split(addr, g, ":")
}
out = ""
for (i = 1; i <= 8; i++) out = out sprintf("%04x", h2d(g[i]))
print out
}'
}
# 32-char hex → IPv6 string (not compressed, but valid for shell variable output).
hex_to_ipv6() {
local h="$1"
printf '%x:%x:%x:%x:%x:%x:%x:%x' \
$(( 0x${h:0:4} )) $(( 0x${h:4:4} )) \
$(( 0x${h:8:4} )) $(( 0x${h:12:4} )) \
$(( 0x${h:16:4} )) $(( 0x${h:20:4} )) \
$(( 0x${h:24:4} )) $(( 0x${h:28:4} ))
}
# Dotted-decimal IPv4 → 8-char hex.
ipv4_to_hex() {
local IFS=.
# shellcheck disable=SC2086
set -- $1
printf '%02x%02x%02x%02x' "$1" "$2" "$3" "$4"
}
# 8-char hex → dotted-decimal IPv4.
hex_to_ipv4() {
local h="$1"
printf '%d.%d.%d.%d' \
$(( 0x${h:0:2} )) $(( 0x${h:2:2} )) \
$(( 0x${h:4:2} )) $(( 0x${h:6:2} ))
}
# Return 0 (true) if the first BITS bits of hex strings H1 and H2 match.
# Mirrors: bmemcmp(h1, h2, bits) == 0
ipv6_prefix_match() {
local h1="$1" h2="$2" bits="$3"
local full=$(( bits / 4 )) rem=$(( bits % 4 ))
[ "${h1:0:$full}" = "${h2:0:$full}" ] || return 1
if [ "$rem" -gt 0 ]; then
# Mask keeping the top REM bits of a nibble:
# rem=1→8, rem=2→12, rem=3→14
local mask=$(( ~((1 << (4 - rem)) - 1) & 0xf ))
[ $(( 0x${h1:$full:1} & mask )) -eq \
$(( 0x${h2:$full:1} & mask )) ] || return 1
fi
return 0
}
# Extract NBITS bits starting at bit FROM (MSB = bit 0) from a 32-char hex
# IPv6 string. Returns the value right-aligned as a decimal integer.
# Mirrors: bmemcpys64 + right-alignment of the result.
# Requires NBITS <= 56 (fits in 64-bit shell arithmetic).
ipv6_extract_bits() {
local hex="$1" from="$2" nbits="$3"
[ "$nbits" -eq 0 ] && { echo 0; return; }
local nib_start=$(( from / 4 ))
local nib_count=$(( (from + nbits - 1) / 4 - nib_start + 1 ))
local chunk="${hex:$nib_start:$nib_count}"
# printf '%d' "0x..." handles hex correctly in bash and busybox.
local val; val=$(printf '%d' "0x$chunk")
local rsh=$(( nib_count * 4 - (from % 4) - nbits ))
echo $(( (val >> rsh) & ((1 << nbits) - 1) ))
}
# Overlay NBITS bits at position FROM (MSB = bit 0) in a 32-char hex string
# with integer VAL (right-aligned). Returns the modified 32-char hex string.
# Mirrors: bmemcpy(dst, src_as_int, nbits) at a given bit offset.
ipv6_set_bits() {
local hex="$1" from="$2" nbits="$3" val="$4"
[ "$nbits" -eq 0 ] && { echo "$hex"; return; }
local nib_start=$(( from / 4 ))
local nib_count=$(( (from + nbits - 1) / 4 - nib_start + 1 ))
local bit_offset=$(( from % 4 ))
local rsh=$(( nib_count * 4 - bit_offset - nbits ))
local nbits_total=$(( nib_count * 4 ))
local cur; cur=$(printf '%d' "0x${hex:$nib_start:$nib_count}")
local width_mask=$(( ~(~0 << nbits_total) ))
local placed=$(( (val << rsh) & width_mask ))
local cleared=$(( cur & ~(((1 << nbits) - 1) << rsh) & width_mask ))
local new_hex; new_hex=$(printf "%0${nib_count}x" $(( cleared | placed )))
echo "${hex:0:$nib_start}${new_hex}${hex:$((nib_start + nib_count))}"
}
# ---------------------------------------------------------------------------
# ubus / PD discovery
# Mirrors handle_dump() + match_prefix() + the PD-search loop.
# ---------------------------------------------------------------------------
_DUMP=""
_DUMP_LOADED=0
_load_dump() {
[ "$_DUMP_LOADED" -eq 0 ] || return 0
_DUMP=$(ubus call network.interface dump 2>/dev/null)
_DUMP_LOADED=1
}
# Search ubus for the longest IPv6 prefix delegation matching the rule.
# On success sets: RESULT_PDLEN, RESULT_PD_HEX, RESULT_IFACE.
find_pd() {
local want_iface="$1" ipv6prefix_hex="$2" prefix6len="$3" lw4o6="$4"
RESULT_PDLEN=-1
RESULT_PD_HEX="00000000000000000000000000000000"
RESULT_IFACE=""
_load_dump
[ -n "$_DUMP" ] || return 1
local idx=0
while true; do
local ifname
ifname=$(echo "$_DUMP" | \
jsonfilter -e "$.interface[$idx].interface" 2>/dev/null)
[ -n "$ifname" ] || break
if [ "$want_iface" = "*" ] || [ "$want_iface" = "$ifname" ]; then
_match_array "$idx" "ipv6-prefix" \
"$ipv6prefix_hex" "$prefix6len" "$ifname" 0
[ "$lw4o6" -eq 1 ] && \
_match_array "$idx" "ipv6-address" \
"$ipv6prefix_hex" "$prefix6len" "$ifname" 1
fi
idx=$(( idx + 1 ))
done
}
# Iterate one ipv6-prefix or ipv6-address array for interface index IDX.
_match_array() {
local iidx="$1" key="$2" ipv6prefix_hex="$3" \
prefix6len="$4" ifname="$5" is_addr="$6"
local pidx=0
while true; do
local paddr pmask
paddr=$(echo "$_DUMP" | \
jsonfilter -e "$.interface[$iidx][\"$key\"][$pidx].address" \
2>/dev/null)
[ -n "$paddr" ] || break
pmask=$(echo "$_DUMP" | \
jsonfilter -e "$.interface[$iidx][\"$key\"][$pidx].mask" \
2>/dev/null)
# lw4o6: treat /128 unicast address as /64 PD (C line 138-139)
[ "$is_addr" -eq 1 ] && [ "$pmask" -eq 128 ] && pmask=64
local phex; phex=$(ipv6_to_hex "$paddr")
if [ "$pmask" -ge "$prefix6len" ] && \
ipv6_prefix_match "$phex" "$ipv6prefix_hex" "$prefix6len"; then
# Longest-prefix wins
if [ "$pmask" -gt "$RESULT_PDLEN" ]; then
RESULT_PDLEN=$pmask
RESULT_PD_HEX=$phex
RESULT_IFACE=$ifname
fi
elif [ "$is_addr" -eq 1 ] && \
[ "$RESULT_PDLEN" -lt "$prefix6len" ] && \
[ "$pmask" -lt "$prefix6len" ] && \
ipv6_prefix_match "$phex" "$ipv6prefix_hex" "$pmask"; then
# lw4o6 fallback: use rule prefix as PD (C lines 145-148)
local pd_hex="00000000000000000000000000000000"
local pfx_val; pfx_val=$(ipv6_extract_bits \
"$ipv6prefix_hex" 0 "$prefix6len")
pd_hex=$(ipv6_set_bits "$pd_hex" 0 "$prefix6len" "$pfx_val")
RESULT_PDLEN=$prefix6len
RESULT_PD_HEX=$pd_hex
RESULT_IFACE=$ifname
fi
pidx=$(( pidx + 1 ))
done
}
# ---------------------------------------------------------------------------
# Main rule-processing loop
# ---------------------------------------------------------------------------
rulecnt=0
status=0
for rule_arg in "$@"; do
# --- Parse comma-separated key=value options (mirrors getsubopt loop) ---
lw4o6=0 fmr=0 ealen=-1 addr4len=32
prefix4len=32 prefix6len=-1 pdlen=-1
ipv4prefix_hex="00000000"
ipv6prefix_hex="00000000000000000000000000000000"
pd_hex="00000000000000000000000000000000"
offset=-1 psidlen=-1 psid=-1
dmr="" br=""
cur_iface="$MAIN_IFACE"
rule="${rule_arg},"
while [ -n "$rule" ]; do
token="${rule%%,*}"; rule="${rule#*,}"
[ -n "$token" ] || continue
key="${token%%=*}"; val="${token#*=}"
[ "$key" = "$token" ] && val=""
case "$key" in
type) [ "$val" = "lw4o6" ] && lw4o6=1 ;;
fmr) fmr=1 ;;
ealen) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 48 ] && ealen=$val ;;
prefix4len) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 32 ] && prefix4len=$val ;;
prefix6len) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 128 ] && prefix6len=$val ;;
ipv4prefix) ipv4prefix_hex=$(ipv4_to_hex "$val") ;;
ipv6prefix) ipv6prefix_hex=$(ipv6_to_hex "$val") ;;
pd) pd_hex=$(ipv6_to_hex "$val") ;;
offset) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 16 ] && offset=$val ;;
psidlen) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 16 ] && psidlen=$val ;;
pdlen) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 128 ] && pdlen=$val ;;
psid) expr "$val" : '^[0-9]*$' >/dev/null 2>&1 && \
[ "$val" -le 65535 ] && psid=$val ;;
dmr) dmr="$val" ;;
br) br="$val" ;;
*) [ -n "$key" ] && \
echo "Skipped invalid option: $key" >&2 ;;
esac
done
# --- Defaults (mirrors C lines 275-283) ---
if [ "$offset" -lt 0 ]; then
if [ "$lw4o6" -eq 1 ]; then offset=0
elif [ "$LEGACY" -eq 1 ]; then offset=4
else offset=6
fi
fi
if [ "$lw4o6" -eq 1 ]; then
[ "$psidlen" -lt 0 ] && psidlen=0
ealen=$psidlen
fi
# --- PD discovery from ubus (mirrors C lines 287-308) ---
if [ "$pdlen" -lt 0 ]; then
find_pd "$MAIN_IFACE" "$ipv6prefix_hex" "$prefix6len" "$lw4o6"
pdlen=$RESULT_PDLEN
pd_hex=$RESULT_PD_HEX
cur_iface=$RESULT_IFACE
fi
# --- Derive ealen (C line 310-311) ---
[ "$ealen" -lt 0 ] && [ "$pdlen" -ge 0 ] && \
ealen=$(( pdlen - prefix6len ))
# --- Derive psidlen (C lines 313-318) ---
if [ "$psidlen" -le 0 ]; then
psidlen=$(( ealen - (32 - prefix4len) ))
[ "$psidlen" -lt 0 ] && psidlen=0
psid=-1
fi
# --- Validate (C line 321, extended: offset+psidlen <= 16 per RFC 7597) ---
if [ "$prefix4len" -lt 0 ] || [ "$prefix6len" -lt 0 ] || \
[ "$ealen" -lt 0 ] || [ "$psidlen" -gt 16 ] || \
[ "$ealen" -lt "$psidlen" ] || \
[ $(( offset + psidlen )) -gt 16 ]; then
echo "Skipping invalid or incomplete rule: $rule_arg" >&2
status=1; continue
fi
# --- Extract PSID from PD (C lines 327-329) ---
if [ "$psid" -lt 0 ] && [ "$psidlen" -ge 0 ] && [ "$pdlen" -ge 0 ]; then
psid=$(ipv6_extract_bits "$pd_hex" \
$(( prefix6len + ealen - psidlen )) "$psidlen")
fi
# --- Normalise PSID (C lines 332-336) ---
# In C, psid arrives left-aligned (from bmemcpys64/be16_to_cpu) so it needs
# >> (16-psidlen) to get the right-aligned value. In shell, ipv6_extract_bits
# already returns a right-aligned integer, so no right-shift is needed.
# psid_value: right-aligned PSID integer (fits in psidlen bits)
# psid: psid_value << (16 - psidlen), left-aligned for port computation
psid_value=0; psid16_hex="0000"
if [ "$psidlen" -gt 0 ]; then
psid_value=$psid
psid16_hex=$(printf '%04x' "$psid_value")
psid=$(( psid_value << (16 - psidlen) ))
fi
# --- IPv4 address (C lines 338-345) ---
# bmemcpys64 extracts ea_bits bits from PD[prefix6len:] left-aligned in 32b,
# then right-shifts by prefix4len, then overlays the top prefix4len bits
# with the IPv4 prefix.
ipv4addr_hex="00000000"; addr4len=32
ea_bits=$(( ealen - psidlen ))
if [ "$pdlen" -ge 0 ] || [ "$ealen" -eq "$psidlen" ]; then
if [ "$ea_bits" -gt 0 ]; then
raw=$(ipv6_extract_bits "$pd_hex" "$prefix6len" "$ea_bits")
ea_shift=$(( 32 - prefix4len - ea_bits ))
ea_placed=$(( raw << ea_shift ))
else
ea_placed=0
fi
host_bits=$(( 32 - prefix4len ))
if [ "$host_bits" -ge 32 ]; then
prefix_mask=0
else
prefix_mask=$(( ~((1 << host_bits) - 1) & 0xffffffff ))
fi
ipv4addr_hex=$(printf '%08x' \
$(( (0x$ipv4prefix_hex & prefix_mask) | ea_placed )))
[ $(( prefix4len + ealen )) -lt 32 ] && \
addr4len=$(( prefix4len + ealen ))
fi
# --- Non-FMR without PD: skip (C lines 347-350) ---
if [ "$pdlen" -lt 0 ] && [ "$fmr" -eq 0 ]; then
echo "Skipping non-FMR without matching PD: $rule_arg" >&2
status=1; continue
fi
# --- CE IPv6 address (C lines 351-356) ---
# Start with zeros; embed IPv4+PSID at byte 10 (or 9 if LEGACY);
# then overlay the first pdlen bits with PD.
ipv6addr_hex="00000000000000000000000000000000"
if [ "$pdlen" -ge 0 ]; then
[ "$LEGACY" -eq 1 ] && v4nib=18 || v4nib=20 # byte→nibble offset
# Set IPv4 (8 nibbles) and PSID (4 nibbles) at the right position
ipv6addr_hex="${ipv6addr_hex:0:$v4nib}${ipv4addr_hex}${psid16_hex}${ipv6addr_hex:$((v4nib+12))}"
# Overlay first pdlen bits with PD (bmemcpy)
full_nibs=$(( pdlen / 4 ))
rem_bits=$(( pdlen % 4 ))
if [ "$rem_bits" -eq 0 ]; then
ipv6addr_hex="${pd_hex:0:$full_nibs}${ipv6addr_hex:$full_nibs}"
else
mask=$(( ~((1 << (4 - rem_bits)) - 1) & 0xf ))
pd_nib=$(( 0x${pd_hex:$full_nibs:1} & mask ))
ad_nib=$(( 0x${ipv6addr_hex:$full_nibs:1} & (~mask & 0xf) ))
mg=$(printf '%x' $(( pd_nib | ad_nib )))
ipv6addr_hex="${pd_hex:0:$full_nibs}${mg}${ipv6addr_hex:$((full_nibs+1))}"
fi
fi
# --- Output ---
rulecnt=$(( rulecnt + 1 ))
printf 'RULE_%d_FMR=%d\n' "$rulecnt" "$fmr"
printf 'RULE_%d_EALEN=%d\n' "$rulecnt" "$ealen"
printf 'RULE_%d_PSIDLEN=%d\n' "$rulecnt" "$psidlen"
printf 'RULE_%d_OFFSET=%d\n' "$rulecnt" "$offset"
printf 'RULE_%d_PREFIX4LEN=%d\n' "$rulecnt" "$prefix4len"
printf 'RULE_%d_PREFIX6LEN=%d\n' "$rulecnt" "$prefix6len"
printf 'RULE_%d_IPV4PREFIX=%s\n' "$rulecnt" "$(hex_to_ipv4 "$ipv4prefix_hex")"
printf 'RULE_%d_IPV6PREFIX=%s\n' "$rulecnt" "$(hex_to_ipv6 "$ipv6prefix_hex")"
if [ "$pdlen" -ge 0 ]; then
printf 'RULE_%d_IPV6PD=%s\n' "$rulecnt" "$(hex_to_ipv6 "$pd_hex")"
printf 'RULE_%d_PD6LEN=%d\n' "$rulecnt" "$pdlen"
printf 'RULE_%d_PD6IFACE=%s\n' "$rulecnt" "$cur_iface"
printf 'RULE_%d_IPV6ADDR=%s\n' "$rulecnt" "$(hex_to_ipv6 "$ipv6addr_hex")"
printf 'RULE_BMR=%d\n' "$rulecnt"
fi
[ "$ipv4addr_hex" != "00000000" ] && {
printf 'RULE_%d_IPV4ADDR=%s\n' "$rulecnt" "$(hex_to_ipv4 "$ipv4addr_hex")"
printf 'RULE_%d_ADDR4LEN=%d\n' "$rulecnt" "$addr4len"
}
# --- Port sets (mirrors C lines 394-407) ---
# All inputs have been validated: offset + psidlen <= 16, so
# suffix_bits >= 0 — the undefined behaviour from the C code cannot occur.
if [ "$psidlen" -gt 0 ] && [ "$psid" -ge 0 ]; then
printf "RULE_%d_PORTSETS='" "$rulecnt"
suffix_bits=$(( 16 - offset - psidlen ))
k=$(( offset ? 1 : 0 ))
k_max=$(( 1 << offset ))
while [ "$k" -lt "$k_max" ]; do
start=$(( (k << (16 - offset)) | (psid >> offset) ))
end=$(( start + (1 << suffix_bits) - 1 ))
[ "$start" -eq 0 ] && start=1
[ "$start" -le "$end" ] && printf '%d-%d ' "$start" "$end"
k=$(( k + 1 ))
done
printf "'\n"
fi
[ -n "$dmr" ] && printf 'RULE_%d_DMR=%s\n' "$rulecnt" "$dmr"
[ -n "$br" ] && printf 'RULE_%d_BR=%s\n' "$rulecnt" "$br"
done
printf 'RULE_COUNT=%d\n' "$rulecnt"
exit $status








コメント