rc: Teach netwait to wait for DAD

In some configurations, especially in jails, it is possible for the
system to boot so fast that we end up launching daemons while duplicate
address detection is still ongoing.  If that happens, said daemons may
fail to bind to IPv6 addresses, as they are still tentative.  Teach the
netwait service to wait (up to 10 seconds, by default) for the tentative
flag to vanish.

MFC after:	1 week
Reviewed by:	olce
Differential Revision:	https://reviews.freebsd.org/D51889
This commit is contained in:
Dag-Erling Smørgrav
2025-10-02 11:28:05 +02:00
parent f7b6ea699b
commit 5ead817c3b
3 changed files with 78 additions and 32 deletions
+2
View File
@@ -500,6 +500,8 @@ netwait_enable="NO" # Enable rc.d/netwait (or NO)
netwait_timeout="60" # Total number of seconds to perform pings.
#netwait_if="" # Wait for active link on each intf in this list.
netwait_if_timeout="30" # Total number of seconds to monitor link state.
netwait_dad="NO" # Wait for DAD to complete
netwait_dad_timeout="10" # Total number of seconds to wait for DAD.
### Miscellaneous network options: ###
icmp_bmcastecho="NO" # respond to broadcast ping packets
+60 -24
View File
@@ -2,12 +2,14 @@
#
# PROVIDE: netwait
# REQUIRE: devd ipfw pf routing
# KEYWORD: nojail
#
# The netwait script helps handle two situations:
# The netwait script helps handle three situations:
# - Systems with USB or other late-attaching network hardware which
# is initialized by devd events. The script waits for all the
# interfaces named in the netwait_if list to appear.
# - Systems with IPv6 addresses, especially jails, where we need to
# wait for DAD to complete before starting daemons, as they will
# otherwise fail to bind to IN6ADDR_ANY.
# - Systems with statically-configured IP addresses in rc.conf(5).
# The IP addresses in the netwait_ip list are pinged. The script
# waits for any single IP in the list to respond to the ping. If your
@@ -29,28 +31,36 @@ netwait_start()
{
local ip rc count output link wait_if got_if any_error
if [ -z "${netwait_if}" ] && [ -z "${netwait_ip}" ]; then
err 1 "No interface or IP addresses listed, nothing to wait for"
if [ -z "${netwait_if}" ] && [ -z "${netwait_ip}" ] &&
! checkyesno netwait_dad ; then
err 1 "Nothing to wait for"
fi
if [ ${netwait_timeout} -lt 1 ]; then
if ! [ "${netwait_if_timeout}" -ge 1 ]; then
err 1 "netwait_if_timeout must be >= 1"
fi
if ! [ "${netwait_dad_timeout}" -ge 1 ]; then
err 1 "netwait_dad_timeout must be >= 1"
fi
if ! [ "${netwait_timeout}" -ge 1 ]; then
err 1 "netwait_timeout must be >= 1"
fi
any_error=false
if [ -n "${netwait_if}" ]; then
any_error=0
for wait_if in ${netwait_if}; do
echo -n "Waiting for ${wait_if}"
link=""
got_if=0
got_if=false
count=1
# Handle SIGINT (Ctrl-C); force abort of while() loop
# Handle SIGINT (Ctrl-C); force abort of while loop
trap break SIGINT
while [ ${count} -le ${netwait_if_timeout} ]; do
if output=`/sbin/ifconfig ${wait_if} 2>/dev/null`; then
if [ ${got_if} -eq 0 ]; then
if ! ${got_if}; then
echo -n ", interface present"
got_if=1
got_if=true
fi
link=`expr "${output}" : '.*[[:blank:]]status: \(no carrier\)'`
if [ -z "${link}" ]; then
@@ -63,22 +73,45 @@ netwait_start()
done
# Restore default SIGINT handler
trap - SIGINT
if [ ${got_if} -eq 0 ]; then
if ! ${got_if}; then
echo ", wait failed: interface never appeared."
any_error=1
any_error=true
elif [ -n "${link}" ]; then
echo ", wait failed: interface still has no link."
any_error=1
any_error=true
fi
done
if [ ${any_error} -eq 1 ]; then
warn "Continuing with startup, but be aware you may not have "
warn "a fully functional networking layer at this point."
fi
fi
if checkyesno netwait_dad; then
got_dad=false
# Handle SIGINT (Ctrl-C); force abort of while loop
trap break SIGINT
echo -n "Waiting for DAD to complete"
count=1
while [ ${count} -le ${netwait_dad_timeout} ]; do
if ! ifconfig | grep -q 'inet6.*tentative'; then
echo ', done.'
got_dad=true
break
fi
sleep 1
count=$((count+1))
done
# Restore default SIGINT handler
trap - SIGINT
if ! ${got_dad}; then
echo ', timed out.'
any_error=true
fi
fi
if [ -n "${netwait_ip}" ]; then
# Handle SIGINT (Ctrl-C); force abort of for() loop
got_ip=false
# Handle SIGINT (Ctrl-C); force abort of for loop
trap break SIGINT
for ip in ${netwait_ip}; do
@@ -90,11 +123,9 @@ netwait_start()
rc=$?
if [ $rc -eq 0 ]; then
# Restore default SIGINT handler
trap - SIGINT
echo ', got response.'
return
got_ip=false
break 2
fi
count=$((count+1))
done
@@ -104,10 +135,15 @@ netwait_start()
# Restore default SIGINT handler
trap - SIGINT
warn "Exhausted IP list. Continuing with startup, but be aware you may"
warn "not have a fully functional networking layer at this point."
if ! ${got_ip}; then
any_error=true
fi
fi
if ${any_error}; then
warn "Continuing with startup, but be aware you may not have "
warn "a fully functional networking layer at this point."
fi
}
load_rc_config $name
+16 -8
View File
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 25, 2025
.Dd October 2, 2025
.Dt RC.CONF 5
.Os
.Sh NAME
@@ -4563,20 +4563,16 @@ If set to
.Dq Li YES ,
delays the start of network-reliant services until
.Va netwait_if
is up and ICMP packets to a destination defined in
is up, duplicate address discovery (DAD) has completed, and ICMP
packets to a destination defined in
.Va netwait_ip
are flowing.
Link state is examined first, followed by
Link state is examined first, followed by DAD, then
.Dq Li pinging
an IP address to verify network usability.
If no destination can be reached or timeouts are exceeded,
network services are started anyway with no guarantee that
the network is usable.
Use of this variable requires both
.Va netwait_ip
and
.Va netwait_if
to be set.
.It Va netwait_ip
.Pq Vt str
Empty by default.
@@ -4612,6 +4608,18 @@ interface if desired.
Defines the total number of seconds to wait for link to become usable,
polled at a 1-second interval.
The default is 30.
.It Va netwait_dad
.Pq Vt str
Set to
.Dq Li NO
by default.
Set to
.Dq Li YES
to enable waiting for DAD to complete.
.It Va netwait_dad_timeout
.Pq Vt int
Indicates the total number of seconds to wait for DAD to complete.
The default is 10.
.It Va rctl_enable
.Pq Vt bool
If set to