]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
hooks: add NOCARRIER_ROAMING reason
authorRoy Marples <roy@marples.name>
Sun, 27 Dec 2020 19:53:31 +0000 (19:53 +0000)
committerRoy Marples <roy@marples.name>
Sun, 27 Dec 2020 19:53:31 +0000 (19:53 +0000)
This is given when the OS supports the concept of wireless roaming
or the IP setup can be persisted when the carrier drops.

When this happens, routes are moved to a higher metric (if supported)
to support non preferred but non roaming routes.
The `interface_order` hook variable will now order the interfaces
according to priority and move roaming interfaces to the back of the
list.
If resolvconf is present then it is called with the -C option
to deprecate DNS and if carrier comes back it is called again with the
-c option to activate it once more.

As part of this change, default route metrics have been changed to
support a larger number of interfaces.
base metric 1000 (was 200)
wireless offset 2000 (was 100)
IPv4LL offset 1000000 (was 10000)
roaming offset 2000000

BUILDING.md
hooks/20-resolv.conf
hooks/dhcpcd-run-hooks.8.in
src/dhcpcd.c
src/if.c
src/ipv4ll.c
src/route.c
src/route.h
src/script.c

index f672724a6680bd6459e1b0bcd22b5b4bcadc6d3f..8b4e1ceae09262243b51fcc52b40b8f1b697b479 100644 (file)
@@ -148,3 +148,11 @@ copied to `$(libexecdir)/dhcpcd-hooks` for use.
 The configure program attempts to find hooks for systems you have installed.
 To add more simply
 `./configure -with-hook=ntp.conf`
+
+If using resolvconf, the `20-resolv.conf` hook now requires a version with the
+`-C` and `-c` options to deprecate and activate interfaces to support wireless
+roaming (Linux) or carrier just drops (NetBSD).
+If your resolvconf does not support this then you will see a warning
+about an illegal option when the carrier changes, but things should still work.
+In this instance the DNS information cannot be Deprecated and may not
+be optimal for multi-homed hosts.
index 73d33386cd91cb4ec438e499c64cc0d2b34f1dbd..86f4873d01fa7cdee3763a3b2675b22a20a9bf1b 100644 (file)
@@ -10,6 +10,11 @@ resolv_conf_dir="$state_dir/resolv.conf"
 NL="
 "
 : ${resolvconf:=resolvconf}
+if type "$resolvconf" >/dev/null 2>&1; then
+       have_resolvconf=true
+else
+       have_resolvconf=false
+fi
 
 build_resolv_conf()
 {
@@ -164,7 +169,7 @@ add_resolv_conf()
        for x in ${new_domain_name_servers}; do
                conf="${conf}nameserver $x$NL"
        done
-       if type "$resolvconf" >/dev/null 2>&1; then
+       if $have_resolvconf; then
                [ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
                printf %s "$conf" | "$resolvconf" -a "$ifname"
                return $?
@@ -180,7 +185,7 @@ add_resolv_conf()
 
 remove_resolv_conf()
 {
-       if type "$resolvconf" >/dev/null 2>&1; then
+       if $have_resolvconf; then
                "$resolvconf" -d "$ifname" -f
        else
                if [ -e "$resolv_conf_dir/$ifname" ]; then
@@ -199,7 +204,11 @@ BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
 esac
 
 if $if_configured; then
-       if $if_up || [ "$reason" = ROUTERADVERT ]; then
+       if $have_resolvconf && [ "$reason" = NOCARRIER_ROAMING ]; then
+               "$resolvconf" -C "$interface.*"
+       elif $have_resolvconf && [ "$reason" = CARRIER ]; then
+               "$resolvconf" -c "$interface.*"
+       elif $if_up || [ "$reason" = ROUTERADVERT ]; then
                add_resolv_conf
        elif $if_down; then
                remove_resolv_conf
index 499157679e7a5ff28affca036a913a3e08e60b61..9b5483a4204d2f66871b9f76c1ba26b80bcb441e 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 24, 2020
+.Dd December 27, 2020
 .Dt DHCPCD-RUN-HOOKS 8
 .Os
 .Sh NAME
@@ -92,6 +92,9 @@ This is generally just a notification and no action need be taken.
 .It Dv NOCARRIER
 dhcpcd lost the carrier.
 The cable may have been unplugged or association to the wireless point lost.
+.It Dv NOCARRIER_ROAMING
+dhcpcd lost the carrier but the interface configuration is persisted.
+The OS has to support wireless roaming or IP Persistance for this to happen.
 .It Dv INFORM | Dv INFORM6
 dhcpcd informed a DHCP server about its address and obtained other
 configuration details.
@@ -147,10 +150,6 @@ will clear the environment variables aside from
 The following variables will then be set, along with any protocol supplied
 ones.
 .Bl -tag -width xnew_delegated_dhcp6_prefix
-.It Ev $chroot
-the directory where
-.Nm dhcpcd
-is chrooted.
 .It Ev $interface
 the name of the interface.
 .It Ev $protocol
@@ -193,12 +192,14 @@ if the
 .Ev interface
 is up, otherwise
 .Dv false .
+This is more than IFF_UP and may not be equal.
 .It Ev $if_down
 .Dv true
 if the
 .Ev interface
 is down, otherwise
 .Dv false .
+This is more than IFF_UP and may not be equal.
 .It Ev $af_waiting
 Address family waiting for, as defined in
 .Xr dhcpcd.conf 5 .
index 1d34cdbb0469208ef5a3a2d4e81abda9d04a3e8f..8762576c13cec90a793d4be11937b2eb396f7e2e 100644 (file)
@@ -700,26 +700,7 @@ dhcpcd_nocarrier_roaming(struct interface *ifp)
 {
 
        loginfox("%s: carrier lost - roaming", ifp->name);
-
-       /*
-        * XXX We should pass something like NOCARRIER_ROAMING
-        * and set if_up=true; ifdown=false; so that the hook scripts
-        * can make a decision to keep or discard the interface information.
-        *
-        * Currently they discard it (no carrier after all) which is
-        * generally fine as new connections won't work and current
-        * connections try to chug along as best as.
-        * dhcpcd has been doing this since NetBSD-7 at least.
-        *
-        * However, for slow roaming this is poor for say web browsing
-        * as new lookups will fail quickly giving a poor user experience.
-        * We should improve this, but the hooks will require some work first
-        * as we need to introduce a mechanism to sort interfaces by
-        * carrier > roaming > nocarrier. Then the hooks know in which
-        * order to apply their data, if at all.
-        * This probably should be a user toggle.
-        */
-       script_runreason(ifp, "NOCARRIER");
+       script_runreason(ifp, "NOCARRIER_ROAMING");
 
 #ifdef ARP
        arp_drop(ifp);
index deb5280b34149c0ef9ef6ea30e79b344bca20ae1..d3852f3dc68e86f4577a1f1a109553994397fa4e 100644 (file)
--- a/src/if.c
+++ b/src/if.c
@@ -697,12 +697,11 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs,
                        ifp->metric = (unsigned int)ifr.ifr_metric;
                if_getssid(ifp);
 #else
-               /* We reserve the 100 range for virtual interfaces, if and when
-                * we can work them out. */
-               ifp->metric = 200 + ifp->index;
+               /* Leave a low portion for user config */
+               ifp->metric = RTMETRIC_BASE + ifp->index;
                if (if_getssid(ifp) != -1) {
                        ifp->wireless = true;
-                       ifp->metric += 100;
+                       ifp->metric += RTMETRIC_WIRELESS;
                }
 #endif
 
index 93898109f7abb090968a4aba102ca7ea5b133d16..6f9cd73d5cf82f84d428be7938dce4078e6ef048 100644 (file)
@@ -137,7 +137,7 @@ ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)
        sa_in_init(&rt->rt_ifa, &state->addr->addr);
        rt->rt_dflags |= RTDF_IPV4LL;
 #ifdef HAVE_ROUTE_METRIC
-       rt->rt_metric += 10000;
+       rt->rt_metric += RTMETRIC_IPV4LL;
 #endif
        return rt_proto_add(routes, rt) ? 1 : 0;
 }
index 47fd6cf2a33dbc15f215e10912473a6738f0b317..71523c146a86c0d3fbd0801009e6ab21d1a1882d 100644 (file)
@@ -168,6 +168,15 @@ rt_compare_proto(void *context, const void *node1, const void *node2)
        if (c != 0)
                return -c;
 
+       /* Prefer roaming over non roaming if both carriers are down. */
+       if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {
+               bool roam1 = if_roaming(ifp1);
+               bool roam2 = if_roaming(ifp2);
+
+               if (roam1 != roam2)
+                       return roam1 ? 1 : -1;
+       }
+
 #ifdef INET
        /* IPv4LL routes always come last */
        if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
@@ -374,6 +383,8 @@ rt_setif(struct rt *rt, struct interface *ifp)
        rt->rt_ifp = ifp;
 #ifdef HAVE_ROUTE_METRIC
        rt->rt_metric = ifp->metric;
+       if (if_roaming(ifp))
+               rt->rt_metric += RTMETRIC_ROAM;
 #endif
 }
 
index e9ac121af3d27d232ed9c8e3f70c535aee29b6c5..cb935c83bdfd54f17a25bc354b20ff434d88170b 100644 (file)
@@ -93,6 +93,15 @@ struct rt {
 #ifdef HAVE_ROUTE_METRIC
        unsigned int            rt_metric;
 #endif
+/* Maximum interface index is generally USHORT_MAX or 65535.
+ * Add some padding for other stuff and we get offsets for the
+ * below that should work automatically.
+ * This is only an issue if the user defines higher metrics in
+ * their configuration, but then they might wish to override also. */
+#define        RTMETRIC_BASE              1000U
+#define        RTMETRIC_WIRELESS          2000U
+#define        RTMETRIC_IPV4LL         1000000U
+#define        RTMETRIC_ROAM           2000000U
 #ifdef HAVE_ROUTE_PREF
        int                     rt_pref;
 #endif
index 0260845e6a3e89e20744b9665fca6f2ccc2874f6..bf1e5152b7a888af6d23b941ea15f4d9b7b44361 100644 (file)
@@ -74,6 +74,9 @@ static const char * const if_params[] = {
        NULL
 };
 
+static const char * true_str = "true";
+static const char * false_str = "false";
+
 void
 if_printoptions(void)
 {
@@ -228,6 +231,10 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
        const struct if_options *ifo;
        const struct interface *ifp2;
        int af;
+       bool is_stdin = ifp->name[0] == '\0';
+       const char *if_up, *if_down;
+       rb_tree_t ifaces;
+       struct rt *rt;
 #ifdef INET
        const struct dhcp_state *state;
 #ifdef IPV4LL
@@ -237,7 +244,6 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
 #ifdef DHCP6
        const struct dhcp6_state *d6_state;
 #endif
-       bool is_stdin = ifp->name[0] == '\0';
 
 #ifdef HAVE_OPEN_MEMSTREAM
        if (ctx->script_fp == NULL) {
@@ -276,6 +282,7 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
                if (efprintf(fp, "pid=%d", getpid()) == -1)
                        goto eexit;
        }
+
        if (!is_stdin) {
                if (efprintf(fp, "reason=%s", reason) == -1)
                        goto eexit;
@@ -326,6 +333,7 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
        else if (strcmp(reason, "PREINIT") == 0 ||
            strcmp(reason, "CARRIER") == 0 ||
            strcmp(reason, "NOCARRIER") == 0 ||
+           strcmp(reason, "NOCARRIER_ROAMING") == 0 ||
            strcmp(reason, "UNKNOWN") == 0 ||
            strcmp(reason, "DEPARTED") == 0 ||
            strcmp(reason, "STOPPED") == 0)
@@ -382,34 +390,43 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
        if (ifp->ctx->options & DHCPCD_DUMPLEASE)
                goto dumplease;
 
+       rb_tree_init(&ifaces, &rt_compare_proto_ops);
+       TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
+               rt = rt_new(UNCONST(ifp2));
+               if (rt == NULL)
+                       goto eexit;
+               if (rb_tree_insert_node(&ifaces, rt) != rt)
+                       goto eexit;
+       }
        if (fprintf(fp, "interface_order=") == -1)
                goto eexit;
-       TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
-               if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
-                       if (fputc(' ', fp) == EOF)
-                               return -1;
-               }
-               if (fprintf(fp, "%s", ifp2->name) == -1)
-                       return -1;
+       RB_TREE_FOREACH(rt, &ifaces) {
+               if (rt != RB_TREE_MIN(&ifaces) &&
+                   fprintf(fp, "%s", " ") == -1)
+                       goto eexit;
+               if (fprintf(fp, "%s", rt->rt_ifp->name) == -1)
+                       goto eexit;
        }
+       rt_headclear(&ifaces, AF_UNSPEC);
        if (fputc('\0', fp) == EOF)
-               return -1;
+               goto eexit;
 
        if (strcmp(reason, "STOPPED") == 0) {
-               if (efprintf(fp, "if_up=false") == -1)
-                       goto eexit;
-               if (efprintf(fp, "if_down=%s",
-                   ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
-                       goto eexit;
+               if_up = false_str;
+               if_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str;
        } else if (strcmp(reason, "TEST") == 0 ||
            strcmp(reason, "PREINIT") == 0 ||
            strcmp(reason, "CARRIER") == 0 ||
            strcmp(reason, "UNKNOWN") == 0)
        {
-               if (efprintf(fp, "if_up=false") == -1)
-                       goto eexit;
-               if (efprintf(fp, "if_down=false") == -1)
-                       goto eexit;
+               if_up = false_str;
+               if_down = false_str;
+       } else if (strcmp(reason, "NOCARRIER") == 0) {
+               if_up = false_str;
+               if_down = true_str;
+       } else if (strcmp(reason, "NOCARRIER_ROAMING") == 0) {
+               if_up = true_str;
+               if_down = false_str;
        } else if (1 == 2 /* appease ifdefs */
 #ifdef INET
            || (protocol == PROTO_DHCP && state && state->new)
@@ -426,16 +443,17 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
 #endif
            )
        {
-               if (efprintf(fp, "if_up=true") == -1)
-                       goto eexit;
-               if (efprintf(fp, "if_down=false") == -1)
-                       goto eexit;
+               if_up = true_str;
+               if_down = false_str;
        } else {
-               if (efprintf(fp, "if_up=false") == -1)
-                       goto eexit;
-               if (efprintf(fp, "if_down=true") == -1)
-                       goto eexit;
+               if_up = false_str;
+               if_down = true_str;
        }
+       if (efprintf(fp, "if_up=%s", if_up) == -1)
+               goto eexit;
+       if (efprintf(fp, "if_down=%s", if_down) == -1)
+               goto eexit;
+
        if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
                if (efprintf(fp, "if_afwaiting=%d", af) == -1)
                        goto eexit;