Page MenuHomeFreeBSD

rtsol: Trigger from devd(8) on media connection
Needs ReviewPublic

Authored by zarychtam_plan-b.pwste.edu.pl on Sun, May 10, 9:02 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, May 17, 4:57 PM
Unknown Object (File)
Sat, May 16, 6:45 PM
Unknown Object (File)
Fri, May 15, 6:34 AM
Unknown Object (File)
Thu, May 14, 8:44 PM
Unknown Object (File)
Thu, May 14, 3:17 PM
Unknown Object (File)
Thu, May 14, 1:37 PM
Unknown Object (File)
Wed, May 13, 9:19 PM
Unknown Object (File)
Wed, May 13, 1:58 PM
Subscribers

Details

Reviewers
pouria
kp
manu
bz
ae
eugen_grosbein.net
Group Reviewers
network
Summary

Run rtsol(8) when media becomes available to complete IPv6
address configuration and acquire a default route faster,
avoiding the wait for the next periodic Router Advertisement.

This emits a single Router Solicitation only if the interface
has the ACCEPT_RTADV nd6 option enabled, although rtsol(8)
also verifies this itself.

The devd(8) rules for dhclient(8) and rtsol(8) were merged into
auto-ip-addr.conf because devd(8) currently does not reliably
support triggering multiple independent actions for the same
event across separate included configuration files.

If such support becomes available in the future, the rules could
be split into separate configuration files for dhclient(8) and
rtsol(8).

Currently, on FreeBSD, IPv6 SLAAC configuration may be significantly delayed when an Ethernet cable is connected after the system has already booted.

This happens because rtsold(8) does not send a Router Solicitation (RS) message upon link/media state change. As a result, IPv6 autoconfiguration depends entirely on periodic Router Advertisements (RA) sent by routers.

This behavior is longstanding and works correctly when the system boots with the Ethernet cable already connected. However, if the cable is plugged in later, the host must wait until the next unsolicited RA arrives. Depending on router configuration, this delay is typically between 3 and 10 minutes.

Usually, we run:

/usr/sbin/rtsold -a -i

Even though rtsold(8) is running and listening on all interfaces, no RS is transmitted after media connection.

Using the -m option improves the situation somewhat because rtsold(8) periodically sends RS messages every minute. However, this still leaves a random delay of up to one minute before SLAAC completes.

In practice, the problem is less visible on busy networks because other hosts frequently send RS messages, which trigger multicast RA responses from the router and indirectly help the FreeBSD host configure itself sooner.

A possible solution would be similar to the existing devd(8) integration used for triggering dhclient on media change. A small devd(8) script could invoke:

rtsol -i <interface>

when link state changes to UP.

This would immediately send a single RS packet and trigger a prompt RA response from the router, provided the interface has the ACCEPT_RTADV nd6 option enabled.

Such behavior would reduce SLAAC configuration time from a random interval (<0;3> or even <0;10> minutes) to effectively immediate configuration after cable insertion.

Edit:

The proposed devd(8) rule should be harmless on systems that do not use ACCEPT_RTADV. In such cases, rtsol(8) immediately exits with:

rtsol: <interface> does not accept Router Advertisement

and no Router Solicitation packet is transmitted.

Unfortunately, there appears to be a separate limitation or design issue in devd(8). LINK_UP events are effectively hijacked by /etc/devd/dhclient.conf, which prevents multiple independent consumers from reliably reacting to the same media-change event.

If this behavior is unintended, it may indicate a devd(8) bug that should be fixed. If it is intentional behavior, then merely splitting the configuration into multiple devd.conf files is not sufficient isolation, since event handling is still not properly composable between independent services such as dhclient and rtsol.

Test Plan

Suggested testing procedure:

  • Start packet capture on the interface:
tcpdump -i <iface> 'icmp6 && (ip6[40] == 133 || ip6[40] == 134)'
  • Plug and unplug the Ethernet cable.
  • Compare behaviour:
    • without the proposed devd(8) hook,
    • with the hook invoking rtsol -i.
  • Observe whether RS (133) packets are transmitted immediately after media connection and whether corresponding RA (134) responses arrive promptly.

If this behavior is not considered suitable for the base system, it may still be useful as an optional port/package targeted at laptops or mobile FreeBSD systems where network media changes are frequent.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

Well this may also result in a lot of unnecessary error logs on systems without IPv6 support (on an interface or at all).

Why not fix rtsold to get the link state change? The infrastructure to do so is all there... I have absolutely no idea if this even compiles but it's likely a start; I'll try to test it tomorrow.

(updated to compile)

diff --git usr.sbin/rtsold/rtsock.c usr.sbin/rtsold/rtsock.c
index 1dfdea0f5c3d..1ea9e962e558 100644
--- usr.sbin/rtsold/rtsock.c
+++ usr.sbin/rtsold/rtsock.c
@@ -59,6 +59,7 @@
 #include "rtsold.h"
 
 static int rtsock_input_ifannounce(int, struct rt_msghdr *, char *);
+static int rtsock_input_ifinfo(int, struct rt_msghdr *, char *);
 
 static struct {
        u_char type;
@@ -67,6 +68,7 @@ static struct {
 } rtsock_dispatch[] = {
        { RTM_IFANNOUNCE, sizeof(struct if_announcemsghdr),
          rtsock_input_ifannounce },
+       { RTM_IFINFO, sizeof(struct if_msghdr), rtsock_input_ifinfo },
        { 0, 0, NULL },
 };
 
@@ -174,3 +176,32 @@ rtsock_input_ifannounce(int s __unused, struct rt_msghdr *rtm, char *lim)
 
        return (0);
 }
+
+static int
+rtsock_input_ifinfo(int s __unused, struct rt_msghdr *rtm, char *lim __unused)
+{
+
+       if (rtm->rtm_type != RTM_IFINFO)
+               return (-1);
+
+       /*
+        * Interface (link) came UP and is configured.  Accelerate RS.
+        * Be minimalistic here; let the state machine handle everything else.
+        */
+       if ((rtm->rtm_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING)) {
+               struct ifinfo *ifi;
+
+               ifi = find_ifinfo(rtm->rtm_index);
+               if (ifi == NULL)
+                       return (0);
+
+               if (dflag > 1) {
+                       warnmsg(LOG_INFO, __func__,
+                           "resetting timer for interface %s in %d state",
+                           ifi->ifname, ifi->state);
+               }
+               rtsol_timer_update(ifi);
+       }
+
+       return (0);
+}
bz requested changes to this revision.Sun, May 10, 10:36 PM
This revision now requires changes to proceed.Sun, May 10, 10:36 PM
In D56927#1303993, @bz wrote:

Well this may also result in a lot of unnecessary error logs on systems without IPv6 support (on an interface or at all).

Of course, this can be avoided by adding an explicit check:

action "ifconfig $subsystem | grep -q 'ACCEPT_RTADV' && rtsol -i $subsystem";

This ensures that rtsol(8) is invoked only on interfaces configured to accept Router Advertisements, avoiding unnecessary log messages on systems without IPv6 support without SLAAC enabled.

However, there is another issue: devd(8) does not merge multiple notify actions with the same priority for the same event. As a result, simply adding a separate configuration file for rtsol(8) does not work reliably alongside the existing /etc/devd/dhclient.conf.

One possible workaround would be extending dhclient.conf itself, but that is not ideal because dhclient.conf belongs to the dhclient package and may be removed entirely on IPv6-only systems. Moreover, some users, especially those running IPv6-only configurations, may intentionally skip installing the DHCP client package or remove it afterwards.

For this reason, the IPv6 SLAAC media-change handling should ideally remain independent from the DHCP client configuration.

In D56927#1303993, @bz wrote:

Well this may also result in a lot of unnecessary error logs on systems without IPv6 support (on an interface or at all).

Why not fix rtsold to get the link state change? The infrastructure to do so is all there... I have absolutely no idea if this even compiles but it's likely a start; I'll try to test it tomorrow.

Thank you for the suggestion. This approach is probably worth pursuing, but it will certainly require more work and testing.

In the meantime, I prepared an updated patch implementing the functionality through devd(8). While this solution is somewhat of a workaround and not as clean as handling link state changes directly in rtsold(8), it works reliably in practice and significantly improves IPv6 autoconfiguration latency after media changes.

Run rtsol(8) when media becomes available to complete IPv6
address configuration and acquire a default route faster,
avoiding the wait for the next periodic Router Advertisement.

This emits a single Router Solicitation only if the interface
has the ACCEPT_RTADV nd6 option enabled, although rtsol(8)
also verifies this itself.

The devd(8) rules for dhclient(8) and rtsol(8) were merged into
auto-ip-addr.conf because devd(8) currently does not reliably
support triggering multiple independent actions for the same
event across separate included configuration files.

If such support becomes available in the future, the rules could
be split into separate configuration files for dhclient(8) and
rtsol(8).