Page MenuHomeFreeBSD

dhclient(8): Add support for IPv6-Only option (RFC 8925)
Needs ReviewPublic

Authored by pouria on Sat, Apr 25, 6:02 PM.
Tags
None
Referenced Files
F154723606: D56637.id.diff
Wed, Apr 29, 7:40 AM
F154447228: D56637.diff
Tue, Apr 28, 3:15 PM
Unknown Object (File)
Tue, Apr 28, 10:28 AM
Unknown Object (File)
Tue, Apr 28, 9:51 AM
Unknown Object (File)
Tue, Apr 28, 9:51 AM
Unknown Object (File)
Tue, Apr 28, 8:56 AM
Unknown Object (File)
Tue, Apr 28, 5:31 AM
Unknown Object (File)
Tue, Apr 28, 4:13 AM

Details

Reviewers
bz
glebius
markj
zlei
ziaee
Group Reviewers
network
Summary

Accept and validate ipv6only option and stop dhcp configuration
process if and only if ipv6 connectivity exists.
Use netlink for ipv6 connectivity check.

Relnotes: yes

I have absolutely no idea about DHCP state machine and its interaction
with dhcp script.
PLEASE REVIEW CAREFULLY.

Test Plan

Set ipv6only option in your dhcp server.
If its value is set to 0xffffffff, dhcp client should not try again.
If it was below max value, it should wait and try again at least
MIN_V6ONLY_WAIT (300) seconds to send another request.
In both cases, it should go_daemon.

% mdo dhclient bridge0
DHCPDISCOVER on bridge0 to 255.255.255.255 port 67 interval 7
DHCPOFFER from 100.64.1.254
IPv6-Only Preferred option received (300 seconds), abort

If option sets after dhclient get bounded, it will renew,
even if the option added later to dhcp server.

DHCPDISCOVER on bridge0 to 255.255.255.255 port 67 interval 3
DHCPOFFER from 100.64.1.254
DHCPREQUEST on bridge0 to 255.255.255.255 port 67
DHCPACK from 100.64.1.254
bound to 100.64.1.125 -- renewal in 300 seconds.
DHCPREQUEST on bridge0 to 100.64.1.254 port 67
DHCPACK from 100.64.1.254
bound to 100.64.1.125 -- renewal in 300 seconds.

So, normally existing clients shouldn't react to option since they simply
send a DHCP REQUEST to renew and we only react to DHCP ACK if we're in INIT-REBOOT state.

% mdo cat /var/db/dhclient.leases.bridge0
lease {
interface "bridge0";
fixed-address 100.64.1.125;
next-server 100.64.1.254;
option subnet-mask 255.255.255.0;
option routers 100.64.1.254;
option domain-name-servers 100.64.1.53,9.9.9.9;
option domain-name "spmzt.net";
option dhcp-lease-time 600;
option dhcp-message-type 5;
option dhcp-server-identifier 100.64.1.254;
option ipv6only 4095;
renew 6 2026/4/25 20:32:56;
rebind 6 2026/4/25 20:36:41;
expire 6 2026/4/25 20:37:56;
}
% mdo dhclient bridge0
DHCPREQUEST on bridge0 to 255.255.255.255 port 67
DHCPACK from 100.64.1.254
bound to 100.64.1.125 -- renewal in 300 seconds.
% mdo pkill dhclient
% mdo dhclient bridge0
DHCPREQUEST on bridge0 to 255.255.255.255 port 67
DHCPACK from 100.64.1.254
IPv6-Only Preferred option received (4095 seconds), abort

If compiled without netlink support, it assumes we don't have connectivity
and ignores the option.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 72547
Build 69430: arc lint + arc unit

Event Timeline

Fix typo in a single log message + rebase main.

  • If the client is in the INIT-REBOOT state and the DHCPv4 server responds with a DHCPACK that includes the IPv6-Only Preferred option, react.
  • Remove extra INET6 macro.

We may want to also update the /etc/rc.d/defaultroute consequently to avoid unnecessary wait time.
But that's another review.

We may want to also update the /etc/rc.d/defaultroute consequently to avoid unnecessary wait time.
But that's another review.

YES PLEASE but also in a way that if there is no IPv4 but 127.1 maybe it doesn't run. People complained about 3 seconds rtsol in the past when I complained about 30 seconds defaultroute. I think further checks in the script and not having DHCP configured anymore made it more bareable if I still have a dual-stack enabled kernel but ... getting it right would be good :)

In D56637#1297542, @bz wrote:

We may want to also update the /etc/rc.d/defaultroute consequently to avoid unnecessary wait time.
But that's another review.

YES PLEASE but also in a way that if there is no IPv4 but 127.1 maybe it doesn't run. People complained about 3 seconds rtsol in the past when I complained about 30 seconds defaultroute. I think further checks in the script and not having DHCP configured anymore made it more bareable if I still have a dual-stack enabled kernel but ... getting it right would be good :)

Unfortunately, I'm not an expert in our /etc/network.subr.
However, I'd like to work on it and see what can I do about it. of course, after this review.

Thank you for adding me to the list of subscribers and thus allowing testing it early.
Today I have tested it in a wired and wireless network, and I can confirm that it works fine. I have not investigated why, but once, after a fresh reboot, the wireless interface got an IPv4 address along with IPv6 - though it never happened to the wired one. I will try to figure out what was going on, perhaps some delays in the wireless network can trigger this.
To make it even smoother, let me propose some enhancements in devd.conf and the defaultroute script - it can improve UX slightly.

--- /etc/rc.d/defaultroute.orig	2026-04-27 00:21:39.995670000 +0200
+++ /etc/rc.d/defaultroute	2026-04-27 08:45:00.451195000 +0200
@@ -36,6 +36,12 @@


 
 	afexists inet || return 0
 
+	# Skip if we support and already have default IPv6 route
+	afexists inet6 && {
+	defif=`get_default_if -inet6`
+	[ -n "${defif}" ] && return
+	}
+	
 	# Return without waiting if we don't have dhcp interfaces or
 	# if none of the dhcp interfaces is plugged in.
 	dhcp_interfaces=`list_net_interfaces dhcp`
--- /etc/devd.conf.orig	2026-04-27 09:14:32.715984000 +0200
+++ /etc/devd.conf	2026-04-27 10:03:57.566192000 +0200
@@ -53,7 +53,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"ethernet";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 #
@@ -72,7 +72,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"802.11";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 # An entry like this might be in a different file, but is included here

Thank you for adding me to the list of subscribers and thus allowing testing it early.
Today I have tested it in a wired and wireless network, and I can confirm that it works fine. I have not investigated why, but once, after a fresh reboot, the wireless interface got an IPv4 address along with IPv6 - though it never happened to the wired one. I will try to figure out what was going on, perhaps some delays in the wireless network can trigger this.

ipv6only option will check if you have a working connection or not.
I guess DORA happens faster than receiving RA in your wifi network.
Is this problem happening all the time or at random?

To make it even smoother, let me propose some enhancements in devd.conf and the defaultroute script - it can improve UX slightly.

--- /etc/rc.d/defaultroute.orig	2026-04-27 00:21:39.995670000 +0200
+++ /etc/rc.d/defaultroute	2026-04-27 08:45:00.451195000 +0200
@@ -36,6 +36,12 @@


 
 	afexists inet || return 0
 
+	# Skip if we support and already have default IPv6 route
+	afexists inet6 && {
+	defif=`get_default_if -inet6`
+	[ -n "${defif}" ] && return
+	}
+	
 	# Return without waiting if we don't have dhcp interfaces or
 	# if none of the dhcp interfaces is plugged in.
 	dhcp_interfaces=`list_net_interfaces dhcp`
--- /etc/devd.conf.orig	2026-04-27 09:14:32.715984000 +0200
+++ /etc/devd.conf	2026-04-27 10:03:57.566192000 +0200
@@ -53,7 +53,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"ethernet";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 #
@@ -72,7 +72,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"802.11";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 # An entry like this might be in a different file, but is included here

Unfortunately, skipping configuration of defaultroute and automatically starting rtsold beside dhcp are depends on administrator.
We're not allowed to do this automatically for user.

Thank you for adding me to the list of subscribers and thus allowing testing it early.
Today I have tested it in a wired and wireless network, and I can confirm that it works fine. I have not investigated why, but once, after a fresh reboot, the wireless interface got an IPv4 address along with IPv6 - though it never happened to the wired one. I will try to figure out what was going on, perhaps some delays in the wireless network can trigger this.

ipv6only option will check if you have a working connection or not.
I guess DORA happens faster than receiving RA in your wifi network.
Is this problem happening all the time or at random?

I only noticed this once on a wireless network, so considering the number of experiments performed, the probability of this occurring was less than 10%.

To make it even smoother, let me propose some enhancements in devd.conf and the defaultroute script - it can improve UX slightly.

--- /etc/rc.d/defaultroute.orig	2026-04-27 00:21:39.995670000 +0200
+++ /etc/rc.d/defaultroute	2026-04-27 08:45:00.451195000 +0200
@@ -36,6 +36,12 @@


 
 	afexists inet || return 0
 
+	# Skip if we support and already have default IPv6 route
+	afexists inet6 && {
+	defif=`get_default_if -inet6`
+	[ -n "${defif}" ] && return
+	}
+	
 	# Return without waiting if we don't have dhcp interfaces or
 	# if none of the dhcp interfaces is plugged in.
 	dhcp_interfaces=`list_net_interfaces dhcp`
--- /etc/devd.conf.orig	2026-04-27 09:14:32.715984000 +0200
+++ /etc/devd.conf	2026-04-27 10:03:57.566192000 +0200
@@ -53,7 +53,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"ethernet";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 #
@@ -72,7 +72,7 @@
 	match "system"		"IFNET";
 	match "type"		"LINK_UP";
 	media-type		"802.11";
-	action "service dhclient quietstart $subsystem";
+	action "service dhclient quietstart $subsystem; service rtsold quietrestart";
 };
 
 # An entry like this might be in a different file, but is included here

Unfortunately, skipping configuration of defaultroute and automatically starting rtsold beside dhcp are depends on administrator.
We're not allowed to do this automatically for user.

I hope that most people testing this solution are not only FreeBSD users but also administrators, and will be able to improve their IPv6-only testing experience this way. You have to admit, waiting for the next periodic RA after plugging in an Ethernet cable can be stressful, as can waiting for the defaultroute script to timeout while booting a laptop. I also believe servers can start once the system has IPv6 network access, so I'll add these fixes to my local repository until the FreeBSD developers resolve the issues.

None of these issues is directly related to this review, and these shortcomings have been present in FreeBSD for a long time, as initially IPv6 connections were marginal, experimental, and didn't provide the real network access services needed. Today, in my opinion, things are different.

So definitely, I haven’t identified any real drawbacks in the proposed solution. Finally, I would like to express my sincere gratitude - I truly appreciate the opportunity to test this upcoming feature.