1414# and append ${VRF_OPTION} so VRF leases see the same table the add uses.
1515#
1616# If option 3 changes across a same-IP RENEW, drop defaults via gateways
17- # that disappeared from $new_routers. Otherwise FRR keeps the old nexthop
18- # and the kernel shows ECMP over old+new. Compare $old_routers vs
19- # $new_routers (same pattern as 98-vyos-static-routes-dhclient-hook).
17+ # that disappeared from $new_routers (including option 3 going away entirely).
18+ # Otherwise FRR keeps the old nexthop and the kernel shows ECMP over old+new.
19+ # Compare $old_routers vs $new_routers (same pattern as
20+ # 98-vyos-static-routes-dhclient-hook). The no-default-route enter hook blanks
21+ # new_routers; removing a leftover option-3 default is the right outcome there
22+ # too, so cleanup runs before the empty-new_routers return.
2023#
2124# Adds still use the enter-hook ip() wrapper so FRR gets tag/distance
22- # IF_METRIC. Honor no-default-route (enter hook blanks new_routers) and
23- # skip when classless static routes are present (stock ignores option 3).
25+ # IF_METRIC. Skip when classless static routes are present (stock ignores
26+ # option 3). Wrapper exit status is not a gate: vtysh "no ip route" fails
27+ # when the route is already gone. Verify the kernel table with /usr/sbin/ip.
2428
2529RUN="yes"
2630
@@ -38,13 +42,15 @@ if [ -n "$new_rfc3442_classless_static_routes" ]; then
3842 return 0 2>/dev/null || exit 0
3943fi
4044
41- if [ -z "$new_routers" ]; then
42- return 0 2>/dev/null || exit 0
43- fi
45+ # Kernel/FRR-installed default present? Real ip(8) only — never the wrapper.
46+ _default_via_present() {
47+ /usr/sbin/ip -4 route show default via "$1" dev "${interface}" ${VRF_OPTION} 2>/dev/null | grep -q .
48+ }
4449
4550if_metric="${IF_METRIC:-210}"
4651
47- # Drop defaults via gateways the server no longer announces.
52+ # Drop defaults via gateways the server no longer announces, including when
53+ # option 3 disappeared (empty new_routers) or no-default-route blanked it.
4854if [ -n "$old_routers" ] && [ "$old_routers" != "$new_routers" ]; then
4955 for router in $old_routers; do
5056 skip=
@@ -61,12 +67,20 @@ if [ -n "$old_routers" ] && [ "$old_routers" != "$new_routers" ]; then
6167 "RENEW/REBIND: removing stale default via ${router} dev ${interface}"
6268 # Wrapper: FRR-aware del (staticd + kernel).
6369 ip -4 route del default via "${router}" dev "${interface}" >/dev/null 2>&1
70+ if _default_via_present "${router}"; then
71+ logmsg warn \
72+ "RENEW/REBIND: stale default still present via ${router} dev ${interface}"
73+ fi
6474 done
6575fi
6676
77+ # Honor no-default-route / missing option 3: nothing to install.
78+ if [ -z "$new_routers" ]; then
79+ return 0 2>/dev/null || exit 0
80+ fi
81+
6782for router in $new_routers; do
68- # Read-only check: real ip(8), not the enter-hook wrapper.
69- if /usr/sbin/ip -4 route show default via "${router}" dev "${interface}" ${VRF_OPTION} 2>/dev/null | grep -q .; then
83+ if _default_via_present "${router}"; then
7084 if_metric=$((if_metric + 1))
7185 continue
7286 fi
@@ -78,5 +92,10 @@ for router in $new_routers; do
7892 ip -4 route add default via "${router}" dev "${interface}" \
7993 metric "${if_metric}" >/dev/null 2>&1
8094
95+ if ! _default_via_present "${router}"; then
96+ logmsg warn \
97+ "RENEW/REBIND: default still missing via ${router} dev ${interface} after add"
98+ fi
99+
81100 if_metric=$((if_metric + 1))
82101done
0 commit comments