背景
在使用OpenWRT 22.03.7系统叠加MWAN3多线负载均衡状况下,双线IPv6地址在正常使用一段时间后,LAN口侧会无形中增加许多IPv6的地址(很多是IPv6-PD前缀已丢弃,但LAN侧未释放问题)导致IPv6中断无法使用。
网络和配置环境
测试系统:iStore OS 22.03.7 2025101516
网络拨号环境:光猫桥接,PPPoE拨号,DHCPv6客户端(@{IPv4拨号接口})
前景铺垫
在网络检索过程中,发现此问题为OpenWRT 22.03通病,在OpenWRT 23.x版本序列中部分修复,OpenWRT 24.x版本序列中完全修复。
结合回答问题推测,核心问题根因
核心问题引起点在odhcpd中,在对iStore OS的代码合并中也发现,iStore OS合并了大量odhcpd相关的修复,但此修复未应用在OpenWRT22.03.x版本序列中。
修复方案
方案1:鉴于OpenWRT官方真的odhcpd做了大量修复及检索关系分析排查,问题大概率出在odhcpd中,因此将odhcpd相关修复代码合并到OpenWRT22.03.x版本序列中应可修复此问题。
方案2:鉴于论坛中已有相类似案例,来源:路由器IPV6地址变动导致wan6掉pd解决方法。结合此方案脚本和本人测试,当IPv6掉网情况下,通过重启虚拟的DHCPv6接口,可恢复网络功能。
因此,编制了一个脚本来实现自动化处理。此脚本支持自己分析IPv6所对应的接口,无需指定,加入了锁机制和一些其它逻辑(感谢AI的DECODE)。脚本可能还存在BUG和意想不到的问题,大家自行选择尝试。适用于单WAN、多WAN等情况,单WAN建议使用论坛中其它佬的版本更简化。
最终。食用方法为在计划任务中添加任务即可。
相关脚本如下:
#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin
[ -r "$0" ] && sed -i 's/\r$//' "$0" 2>/dev/null
. /lib/functions/network.sh 2>/dev/null || true
STATE_FILE="/tmp/last_ipv6_pd_strict"
log() { ts=$(date '+%Y-%m-%d %H:%M:%S'); echo "$ts ipv6pdcheck $*"; if command -v logger >/dev/null 2>&1; then logger -t ipv6pdcheck "$*"; fi; }
LOCK_BASE="/tmp/ipv6pd.lock"
LOCK_ID="$$"
MASTER_LOCK="/tmp/ipv6pd.master.lock"
acquire_master_lock() {
pdir=$(dirname "$MASTER_LOCK")
mkdir -p "$pdir" 2>/dev/null
log "lock_master_try id=$LOCK_ID dir=$MASTER_LOCK"
if mkdir "$MASTER_LOCK" 2>/dev/null; then
ts=$(date +%s)
echo "$ts" > "$MASTER_LOCK/ts"
echo "$LOCK_ID" > "$MASTER_LOCK/id"
log "lock_master_acquired id=$LOCK_ID ts=$ts dir=$MASTER_LOCK"
return 0
fi
if [ -f "$MASTER_LOCK/id" ]; then
hid=$(cat "$MASTER_LOCK/id" 2>/dev/null)
if kill -0 "$hid" 2>/dev/null; then
log "lock_master_busy holder_id=$hid dir=$MASTER_LOCK"
return 1
else
log "lock_master_stale holder_id=$hid dir=$MASTER_LOCK"
rm -rf "$MASTER_LOCK"
mkdir "$MASTER_LOCK" 2>/dev/null || return 1
ts=$(date +%s)
echo "$ts" > "$MASTER_LOCK/ts"
echo "$LOCK_ID" > "$MASTER_LOCK/id"
log "lock_master_reacquired id=$LOCK_ID ts=$ts dir=$MASTER_LOCK"
return 0
fi
fi
return 1
}
release_master_lock() { log "lock_master_release id=$LOCK_ID dir=$MASTER_LOCK"; rm -rf "$MASTER_LOCK"; }
get_cooldown() { i="$1"; case "$i" in lan) echo 3;; *) echo 5;; esac; }
get_stale_sec() { i="$1"; case "$i" in lan) echo 0;; *) echo 10;; esac; }
acquire_lock_for() {
i="$1"
LD="$LOCK_BASE.$i"
pdir=$(dirname "$LD")
mkdir -p "$pdir" 2>/dev/null
log "lock_try id=$LOCK_ID iface=$i dir=$LD"
if mkdir "$LD" 2>/dev/null; then
ts=$(date +%s)
echo "$ts" > "$LD/ts"
echo "$LOCK_ID" > "$LD/id"
log "lock_acquired id=$LOCK_ID ts=$ts iface=$i dir=$LD"
return 0
fi
if [ -f "$LD/ts" ]; then
now=$(date +%s)
ts=$(cat "$LD/ts")
age=$((now - ts))
STALE=$(get_stale_sec "$i")
if [ "$age" -gt "$STALE" ]; then
log "lock_stale_remove ts=$ts age=$age now=$now iface=$i dir=$LD"
rm -rf "$LD"
mkdir "$LD" 2>/dev/null || return 1
nts=$(date +%s)
echo "$nts" > "$LD/ts"
echo "$LOCK_ID" > "$LD/id"
log "lock_reacquired id=$LOCK_ID ts=$nts iface=$i dir=$LD"
return 0
else
hid=$(cat "$LD/id" 2>/dev/null)
if kill -0 "$hid" 2>/dev/null; then
log "lock_busy holder_id=$hid ts=$ts age=$age iface=$i dir=$LD"
else
log "lock_stale_remove_dead holder_id=$hid ts=$ts age=$age iface=$i dir=$LD"
rm -rf "$LD"
mkdir "$LD" 2>/dev/null || return 1
nts=$(date +%s)
echo "$nts" > "$LD/ts"
echo "$LOCK_ID" > "$LD/id"
log "lock_reacquired id=$LOCK_ID ts=$nts iface=$i dir=$LD"
return 0
fi
fi
else
log "lock_busy_no_ts iface=$i dir=$LD"
fi
return 1
}
release_lock_for() {
i="$1"
LD="$LOCK_BASE.$i"
log "lock_release id=$LOCK_ID iface=$i dir=$LD"
rm -rf "$LD"
}
if ! acquire_master_lock; then log "instance_running_master"; exit 0; fi
trap 'release_master_lock' EXIT
# Enforce a global cooldown to prevent flapping
GLOBAL_COOLDOWN_FILE="/tmp/ipv6pd_global_cooldown"
GLOBAL_COOLDOWN_SECONDS=55 # Almost a minute
# Check for global cooldown after acquiring master lock
if [ -f "$GLOBAL_COOLDOWN_FILE" ]; then
last_run_ts=$(cat "$GLOBAL_COOLDOWN_FILE" 2>/dev/null)
if [ -n "$last_run_ts" ]; then
now_ts=$(date +%s)
age=$((now_ts - last_run_ts))
if [ "$age" -lt "$GLOBAL_COOLDOWN_SECONDS" ]; then
log "global_cooldown_active age=$age, skipping run"
exit 0
fi
fi
fi
# Update the global cooldown timestamp at the beginning of a valid run
mkdir -p "$(dirname "$GLOBAL_COOLDOWN_FILE")" 2>/dev/null
date +%s > "$GLOBAL_COOLDOWN_FILE"
IFACES=$(ubus list network.interface.* | sed 's/network.interface.//')
DIALS=""
for i in $IFACES; do
s=$(ubus call network.interface.$i status)
p=$(echo "$s" | jsonfilter -e '@["proto"]')
if [ "$p" = "dhcpv6" ]; then
DIALS="$DIALS $i"
fi
done
[ -z "$DIALS" ] && log "no_dial_ifaces"
type network_flush_cache >/dev/null 2>&1 && network_flush_cache || true
if type network_get_device >/dev/null 2>&1; then network_get_device LAN_DEV "lan"; fi
[ -z "$LAN_DEV" ] && LAN_DEV=$(ubus call network.interface.lan status | jsonfilter -e '@["l3_device"]')
[ -z "$LAN_DEV" ] && log "lan_dev_not_found" && exit 0
ipv6_to_hex() { awk -v ip="$1" 'function pad4(s){s=tolower(s);n=length(s);if(n==0)return "0000";if(n==1)return "000" s;if(n==2)return "00" s;if(n==3)return "0" s;return s} BEGIN{ split(ip,a,"/"); ip=a[1]; gsub(/[[:space:]]/,"",ip); if (index(ip,"::")){ split(ip,parts,"::"); left=parts[1]; right=parts[2]; lc=split(left,l,":"); if (right!="") rc=split(right,r,":"); else rc=0; } else { left=ip; lc=split(left,l,":"); rc=0; } missing=8-lc-rc; res=""; for(i=1;i<=lc;i++) res=res pad4(l[i]); for(i=1;i<=missing;i++) res=res "0000"; for(i=1;i<=rc;i++) res=res pad4(r[i]); print res; }'; }
h2d() { case "$1" in 0) echo 0;; 1) echo 1;; 2) echo 2;; 3) echo 3;; 4) echo 4;; 5) echo 5;; 6) echo 6;; 7) echo 7;; 8) echo 8;; 9) echo 9;; a|A) echo 10;; b|B) echo 11;; c|C) echo 12;; d|D) echo 13;; e|E) echo 14;; f|F) echo 15;; *) echo 0;; esac; }
norm_list() { echo "$1" | tr ' ' '\n' | sed '/^$/d' | sort | tr '\n' ' ' | sed 's/[[:space:]]*$//'; }
uniq_list() { echo "$1" | tr ' ' '\n' | sed '/^$/d' | awk '!seen[$0]++' | tr '\n' ' ' | sed 's/[[:space:]]*$//'; }
prefix_match() {
addr="$1"; pd="$2"; mask="$3"
ahex=$(ipv6_to_hex "$addr")
phex=$(ipv6_to_hex "$pd")
n=$((mask/4)); r=$((mask%4))
apre=$(echo "$ahex" | cut -c 1-$n)
ppre=$(echo "$phex" | cut -c 1-$n)
[ "$apre" != "$ppre" ] && echo 0 && return
if [ "$r" -eq 0 ]; then echo 1; return; fi
an=$(echo "$ahex" | cut -c $((n+1))-$((n+1)))
pn=$(echo "$phex" | cut -c $((n+1))-$((n+1)))
av=$(h2d "$an"); pv=$(h2d "$pn")
shift=$((4 - r))
if [ "$shift" -eq 1 ]; then div=2; elif [ "$shift" -eq 2 ]; then div=4; elif [ "$shift" -eq 3 ]; then div=8; else div=1; fi
[ $((pv / div)) -eq $((av / div)) ] && echo 1 || echo 0
}
match_any() {
addr="$1"; res=0
for pc in $PD_LIST; do
pa=${pc%/*}; pm=${pc#*/}
m=$(prefix_match "$addr" "$pa" "$pm")
[ "$m" = "1" ] && res=1 && break
done
echo $res
}
log "lan_dev=$LAN_DEV"
s_lan=$(ubus call network.interface.lan status)
LAN64_KEYS=""
LAN64_UNIQ_ADDRS=""
ULA64_KEYS=""
ULA64_UNIQ_ADDRS=""
for idx in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49; do
a=$(echo "$s_lan" | jsonfilter -e "@[\"ipv6-address\"][${idx}][\"address\"]")
m=$(echo "$s_lan" | jsonfilter -e "@[\"ipv6-address\"][${idx}][\"mask\"]")
[ -z "$a" ] && continue
[ "$m" != "64" ] && continue
case "$a" in fe80:*) continue;; esac
hex=$(ipv6_to_hex "$a")
key=$(echo "$hex" | cut -c 1-16)
case "$a" in fd*|fc*)
exist=0
for k in $ULA64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && ULA64_KEYS="$ULA64_KEYS $key" && ULA64_UNIQ_ADDRS="$ULA64_UNIQ_ADDRS $a"
;;
*)
exist=0
for k in $LAN64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && LAN64_KEYS="$LAN64_KEYS $key" && LAN64_UNIQ_ADDRS="$LAN64_UNIQ_ADDRS $a"
;;
esac
done
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*) continue;; esac
hex=$(ipv6_to_hex "$addr")
key=$(echo "$hex" | cut -c 1-16)
case "$addr" in fd*|fc*)
exist=0
for k in $ULA64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && ULA64_KEYS="$ULA64_KEYS $key" && ULA64_UNIQ_ADDRS="$ULA64_UNIQ_ADDRS $addr"
;;
*)
exist=0
for k in $LAN64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && LAN64_KEYS="$LAN64_KEYS $key" && LAN64_UNIQ_ADDRS="$LAN64_UNIQ_ADDRS $addr"
;;
esac
done
LAN64_COUNT=$(echo "$LAN64_UNIQ_ADDRS" | sed '/^$/d' | wc -l)
ULA64_COUNT=$(echo "$ULA64_UNIQ_ADDRS" | sed '/^$/d' | wc -l)
log "lan64_count=$LAN64_COUNT"
log "lan64_ula_count=$ULA64_COUNT"
PD_LIST=""
ASSIGNED64_COUNT=0
ASSIGNED_KEYS=""
PD_GROUPS=""
PD_NORM_KEYS=""
for i in $DIALS; do
s=$(ubus call network.interface.$i status)
for idx in 0 1 2 3 4 5 6 7 8 9; do
A=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix\"][${idx}][\"address\"]")
M=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix\"][${idx}][\"mask\"]")
[ -z "$A" ] && continue
[ -z "$M" ] && continue
pair="$A/$M"
exist=0
for e in $PD_LIST; do [ "$e" = "$pair" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && PD_LIST="$PD_LIST $pair"
phex=$(ipv6_to_hex "$A")
norm="$phex/$M"
ng=0
for nk in $PD_NORM_KEYS; do [ "$nk" = "$norm" ] && ng=1 && break; done
if [ "$ng" -eq 0 ]; then
PD_NORM_KEYS="$PD_NORM_KEYS $norm"
PD_GROUPS="$PD_GROUPS $norm@$i"
fi
done
for j in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
PA=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix-assignment\"][${j}][\"address\"]")
PM=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix-assignment\"][${j}][\"mask\"]")
[ -z "$PA" ] && continue
[ "$PM" != "64" ] && continue
h=$(ipv6_to_hex "$PA")
k=$(echo "$h" | cut -c 1-16)
ex=0
for kk in $ASSIGNED_KEYS; do [ "$kk" = "$k" ] && ex=1 && break; done
if [ "$ex" -eq 0 ]; then ASSIGNED_KEYS="$ASSIGNED_KEYS $k"; ASSIGNED64_COUNT=$((ASSIGNED64_COUNT + 1)); fi
done
done
if [ -z "$PD_LIST" ]; then
NEW_STATE="$LAN64_COUNT $ASSIGNED64_COUNT 0 $PD_LIST"
PREV_STATE=$(cat "$STATE_FILE" 2>/dev/null)
log "pd_list_empty"
mkdir -p "$(dirname "$STATE_FILE")" 2>/dev/null
echo "$NEW_STATE" > "$STATE_FILE"
log "state_updated"
exit 0
fi
log "dial_ifaces=$DIALS pd_list=$PD_LIST assigned64_count=$ASSIGNED64_COUNT"
log "pd_groups=$PD_GROUPS"
iface_pd_present() {
s=$(ubus call network.interface.$1 status)
a=$(echo "$s" | jsonfilter -e '@["ipv6-prefix"][*]["address"]' | sed '/^$/d' | wc -l)
[ "$a" -gt 0 ] && echo 1 || echo 0
}
can_restart_iface() {
i="$1"
f="/tmp/ipv6pd.cooldown.$i"
now=$(date +%s)
if [ -f "$f" ]; then
last=$(cat "$f")
diff=$((now - last))
CD=$(get_cooldown "$i")
[ "$diff" -lt "$CD" ] && echo 0 && return
fi
echo 1
}
update_cooldown() { i="$1"; mkdir -p /tmp 2>/dev/null; date +%s > "/tmp/ipv6pd.cooldown.$i"; }
refresh_pd_info() {
PD_LIST=""
ASSIGNED64_COUNT=0
ASSIGNED_KEYS=""
PD_GROUPS=""
PD_NORM_KEYS=""
for i in $DIALS; do
s=$(ubus call network.interface.$i status)
for idx in 0 1 2 3 4 5 6 7 8 9; do
A=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix\"][${idx}][\"address\"]")
M=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix\"][${idx}][\"mask\"]")
[ -z "$A" ] && continue
[ -z "$M" ] && continue
pair="$A/$M"
exist=0
for e in $PD_LIST; do [ "$e" = "$pair" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && PD_LIST="$PD_LIST $pair"
phex=$(ipv6_to_hex "$A")
norm="$phex/$M"
ng=0
for nk in $PD_NORM_KEYS; do [ "$nk" = "$norm" ] && ng=1 && break; done
if [ "$ng" -eq 0 ]; then
PD_NORM_KEYS="$PD_NORM_KEYS $norm"
PD_GROUPS="$PD_GROUPS $norm@$i"
fi
done
for j in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
PA=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix-assignment\"][${j}][\"address\"]")
PM=$(echo "$s" | jsonfilter -e "@[\"ipv6-prefix-assignment\"][${j}][\"mask\"]")
[ -z "$PA" ] && continue
[ "$PM" != "64" ] && continue
h=$(ipv6_to_hex "$PA")
k=$(echo "$h" | cut -c 1-16)
ex=0
for kk in $ASSIGNED_KEYS; do [ "$kk" = "$k" ] && ex=1 && break; done
if [ "$ex" -eq 0 ]; then ASSIGNED_KEYS="$ASSIGNED_KEYS $k"; ASSIGNED64_COUNT=$((ASSIGNED64_COUNT + 1)); fi
done
done
}
cleanup_lan_nonmatching() {
[ -z "$PD_LIST" ] && return
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
if [ "$mm" != "1" ]; then
ip -6 addr del "$addr/$mask" dev "$LAN_DEV" 2>/dev/null
log "lan_addr_removed $addr/$mask"
fi
done
}
lan_gua_limit() { di=$(echo "$DIALS" | tr ' ' '\n' | sed '/^$/d' | wc -l); [ "$di" -lt 1 ] && di=1; echo "$di"; }
prune_excess_lan_gua() {
limit=$(lan_gua_limit)
c=0
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
[ "$mm" != "1" ] && continue
c=$((c+1))
if [ "$c" -gt "$limit" ]; then
ip -6 addr del "$addr/$mask" dev "$LAN_DEV" 2>/dev/null
log "lan_addr_pruned $addr/$mask"
fi
done
}
recalc_lan_counts() {
LAN64_KEYS=""; LAN64_UNIQ_ADDRS=""; ULA64_KEYS=""; ULA64_UNIQ_ADDRS=""
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*) continue;; esac
hex=$(ipv6_to_hex "$addr")
key=$(echo "$hex" | cut -c 1-16)
case "$addr" in fd*|fc*)
exist=0
for k in $ULA64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && ULA64_KEYS="$ULA64_KEYS $key" && ULA64_UNIQ_ADDRS="$ULA64_UNIQ_ADDRS $addr"
;;
*)
exist=0
for k in $LAN64_KEYS; do [ "$k" = "$key" ] && exist=1 && break; done
[ "$exist" -eq 0 ] && LAN64_KEYS="$LAN64_KEYS $key" && LAN64_UNIQ_ADDRS="$LAN64_UNIQ_ADDRS $addr"
;;
esac
done
LAN64_COUNT=$(echo "$LAN64_UNIQ_ADDRS" | sed '/^$/d' | wc -l)
ULA64_COUNT=$(echo "$ULA64_UNIQ_ADDRS" | sed '/^$/d' | wc -l)
}
restart_and_check_iface() {
i="$1"
cr=$(can_restart_iface "$i")
if [ "$cr" != "1" ]; then
log "iface=$i cooldown_skip"
return 0
fi
attempt=1
max_attempt=3
while [ $attempt -le $max_attempt ]; do
log "iface=$i restart_attempt=$attempt"
update_cooldown "$i"
ifdown "$i"
sleep 1
ifup "$i"
t=0
present=0
while [ $t -lt 10 ]; do
t=$((t+1))
sleep 2
pm=$(iface_pd_present "$i")
if [ "$pm" = "1" ]; then present=1; break; fi
done
if [ "$present" = "1" ]; then
log "iface=$i pd_present"
break
else
log "iface=$i pd_missing_after_wait"
fi
attempt=$((attempt+1))
done
if [ "$present" != "1" ]; then
log "iface=$i pd_missing_final"
return 1
fi
return 0
}
MISMATCH=0
for la in $LAN64_UNIQ_ADDRS; do
mm=$(match_any "$la")
[ "$mm" != "1" ] && MISMATCH=1 && break
done
RESTART_ALL=0
[ "$MISMATCH" -eq 1 ] && RESTART_ALL=1
REASON="none"
[ "$MISMATCH" -eq 1 ] && REASON="mismatch"
log "precheck mismatch=$MISMATCH lan64=$LAN64_COUNT assigned=$ASSIGNED64_COUNT reason=$REASON"
PREV_STATE=$(cat "$STATE_FILE" 2>/dev/null)
PREV_PD_LIST=$(echo "$PREV_STATE" | cut -d ' ' -f 4-)
CUR_PD_NORM=$(norm_list "$PD_LIST")
PREV_PD_NORM=$(norm_list "$PREV_PD_LIST")
PD_CHANGE=0
[ "$CUR_PD_NORM" != "$PREV_PD_NORM" ] && PD_CHANGE=1
if [ "$PD_CHANGE" -eq 1 ]; then
[ "$REASON" = "none" ] && REASON="pd_change"
RESTART_ALL=1
fi
NEW_STATE="$LAN64_COUNT $ASSIGNED64_COUNT $MISMATCH $PD_LIST"
if [ "$PREV_STATE" != "$NEW_STATE" ]; then
log "state_change_detected prev=$PREV_STATE new=$NEW_STATE reason=$REASON pd_change=$PD_CHANGE"
if [ "$RESTART_ALL" -eq 1 ] && [ "$PD_CHANGE" -eq 1 ]; then
log "lock_force_delete_all base=$LOCK_BASE"
rm -rf ${LOCK_BASE}.* 2>/dev/null
fi
fi
prev_assigned=$(echo "$PREV_STATE" | awk '{print $2}')
if [ "$RESTART_ALL" -ne 1 ] && [ "$ASSIGNED64_COUNT" -eq 0 ] && [ "$prev_assigned" = "0" ] && [ -n "$PD_LIST" ] && [ "$LAN64_COUNT" -eq 0 ]; then
log "force_restart_due_to_assign_missing_persistent"
for i in $DIALS; do
if ! acquire_lock_for "$i"; then
log "iface=$i instance_running"
continue
fi
if ! restart_and_check_iface "$i"; then
release_lock_for "$i"
log "iface=$i assign_missing_restart_failed"
continue
fi
release_lock_for "$i"
done
/etc/init.d/odhcpd restart
refresh_pd_info
cleanup_lan_nonmatching
prune_excess_lan_gua
recalc_lan_counts
fi
if [ "$RESTART_ALL" -ne 1 ]; then
if [ "$LAN64_COUNT" -eq 0 ] && [ -n "$PD_LIST" ]; then
log "lan_no_gua_bounce"
cr=$(can_restart_iface "lan")
if [ "$cr" = "1" ]; then
update_cooldown "lan"
ifdown lan
sleep 1
ifup lan
/etc/init.d/odhcpd restart
refresh_pd_info
cleanup_lan_nonmatching
prune_excess_lan_gua
recalc_lan_counts
else
log "lan_bounce_cooldown_skip"
fi
fi
if [ "$ASSIGNED64_COUNT" -eq 0 ] && [ -n "$PD_LIST" ] && [ "$LAN64_COUNT" -eq 0 ]; then
log "lan_assign_missing_bounce"
cr=$(can_restart_iface "lan")
if [ "$cr" = "1" ]; then
update_cooldown "lan"
ifdown lan
sleep 1
ifup lan
/etc/init.d/odhcpd restart
refresh_pd_info
cleanup_lan_nonmatching
prune_excess_lan_gua
recalc_lan_counts
else
log "lan_bounce_cooldown_skip"
fi
fi
cleanup_lan_nonmatching
prune_excess_lan_gua
recalc_lan_counts
fi
if [ "$RESTART_ALL" -eq 1 ]; then
RTARGETS=""
for pg in $PD_GROUPS; do i=${pg#*@}; RTARGETS="$RTARGETS $i"; done
for i in $DIALS; do
found=0
for t in $RTARGETS; do [ "$t" = "$i" ] && found=1 && break; done
[ "$found" -eq 0 ] && RTARGETS="$RTARGETS $i"
done
RTARGETS=$(uniq_list "$RTARGETS")
log "restart begin dial_ifaces=$DIALS targets=$RTARGETS"
for i in $RTARGETS; do
if ! acquire_lock_for "$i"; then
log "iface=$i instance_running"
continue
fi
if ! restart_and_check_iface "$i"; then
release_lock_for "$i"
log "iface=$i pd_missing_final_stop_sequence"
break
fi
release_lock_for "$i"
done
/etc/init.d/odhcpd restart
refresh_pd_info
good=0
t=0
while [ $t -lt 20 ]; do
t=$((t+1))
sleep 2
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
if [ "$mm" = "1" ]; then good=1; break; fi
done
[ "$good" = "1" ] && break
done
if [ "$good" != "1" ]; then
log "lan_gua_missing_bounce"
cr=$(can_restart_iface "lan")
if [ "$cr" = "1" ]; then
update_cooldown "lan"
ifdown lan
sleep 1
ifup lan
/etc/init.d/odhcpd restart
refresh_pd_info
t=0
good=0
while [ $t -lt 20 ]; do
t=$((t+1))
sleep 2
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
if [ "$mm" = "1" ]; then good=1; break; fi
done
[ "$good" = "1" ] && break
done
if [ "$good" = "1" ]; then
refresh_pd_info
log "lan_gua_present_after_bounce"
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
if [ "$mm" != "1" ]; then
ip -6 addr del "$addr/$mask" dev "$LAN_DEV" 2>/dev/null
log "lan_addr_removed $addr/$mask"
fi
done
else
log "lan_gua_missing_final"
fi
else
log "lan_bounce_cooldown_skip"
fi
else
refresh_pd_info
log "lan_gua_present"
LIST=$(ip -6 addr show dev "$LAN_DEV" | awk '/inet6 /{print $2}')
for ent in $LIST; do
addr=${ent%/*}; mask=${ent#*/}
[ -z "$addr" ] && continue
[ "$mask" != "64" ] && continue
case "$addr" in fe80:*|fd*|fc*) continue;; esac
mm=$(match_any "$addr")
if [ "$mm" != "1" ]; then
ip -6 addr del "$addr/$mask" dev "$LAN_DEV" 2>/dev/null
log "lan_addr_removed $addr/$mask"
fi
done
fi
log "restart done targets=$RTARGETS"
fi
cleanup_lan_nonmatching
recalc_lan_counts
mkdir -p "$(dirname "$STATE_FILE")" 2>/dev/null
echo "$LAN64_COUNT $ASSIGNED64_COUNT $MISMATCH $PD_LIST" > "$STATE_FILE"
log "state_updated"
forward=$(sysctl -n net.ipv6.conf.all.forwarding 2>/dev/null)
[ "$forward" != "1" ] && sysctl -w net.ipv6.conf.all.forwarding=1 >/dev/null 2>&1 && log "ipv6_forwarding_enabled"
rad=$(uci -q get odhcpd.@odhcpd[0].ra_default 2>/dev/null)
[ "$rad" != "1" ] && uci -q set odhcpd.@odhcpd[0].ra_default='1' && uci commit odhcpd && /etc/init.d/odhcpd restart && log "odhcpd_ra_default_enabled"
dr=$(ip -6 route show default | wc -l)
if [ "$dr" -lt 1 ]; then
log "ipv6_default_route_missing"
for i in $DIALS; do
if ! acquire_lock_for "$i"; then
log "iface=$i instance_running"
continue
fi
log "iface=$i force_restart_due_to_missing_default"
attempt=1
max_attempt=2
while [ $attempt -le $max_attempt ]; do
ifdown "$i"
sleep 1
ifup "$i"
t=0
present=0
while [ $t -lt 10 ]; do
t=$((t+1))
sleep 2
pm=$(iface_pd_present "$i")
if [ "$pm" = "1" ]; then present=1; break; fi
done
[ "$present" = "1" ] && break
attempt=$((attempt+1))
done
release_lock_for "$i"
done
/etc/init.d/odhcpd restart
refresh_pd_info
cleanup_lan_nonmatching
prune_excess_lan_gua
recalc_lan_counts
fi

