]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Linux: use broadcast flag with ipvlan interfaces by default.
authorIdo Rosen <ido@kernel.org>
Fri, 23 Apr 2021 09:34:34 +0000 (05:34 -0400)
committerIdo Rosen <ido@kernel.org>
Sun, 25 Apr 2021 10:02:53 +0000 (06:02 -0400)
Linux ipvlan interfaces share a MAC address with their siblings and
parent physical interface.  Before they are assigned an IP address,
these virtual interfaces do not receive DHCP OFFER unicast messages
because the ipvlan driver does not know to pass them to the virtual
interface yet by IP.  This chicken-and-egg problem is resolved with
two changes:

In this patch, we set the broadcast flag for an interface if it
belongs to the ipvlan driver, as detected via SIOCETHTOOL ETHTOOL_GDRVINFO.
(closes #32)

A forthcoming patch will automatically modify the DHCP IAID for
ipvlan interfaces so that they do not conflict with the parent
(lower/physical) interface IAID.  For now, dhcpcd will display a warning
log message when conflicting IAID (same MAC address) interfaces are active.

(A minor grammar correction is included free of charge.)

src/common.h
src/dhcpcd.8.in
src/dhcpcd.c
src/if-linux.c

index ff8f3f8b943b8d720a4ff872771ff1d689125fa5..38740164987af0f48a619d10dca3f2ce60de5396 100644 (file)
 #define ROUNDUP4(a)            (1 + (((a) - 1) |  3))
 #define ROUNDUP8(a)            (1 + (((a) - 1) |  7))
 
+#ifndef FIELD_SIZEOF
+#define FIELD_SIZEOF(t, f) sizeof(((t *)0)->f)
+#endif
+
 /* Some systems don't define timespec macros */
 #ifndef timespecclear
 #define timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
index c307f69f0428d9f23b9e6e5d36310f8d2e6f2ab7..81e19e6afcdbbaf8d58213ad265da927637007b2 100644 (file)
@@ -651,7 +651,7 @@ of a randomly generated number.
 .It Fl J , Fl Fl broadcast
 Instructs the DHCP server to broadcast replies back to the client.
 Normally this is only set for non-Ethernet interfaces,
-such as FireWire and InfiniBand.
+such as FireWire and InfiniBand, and on Linux-based systems for ipvlan as well.
 In most instances,
 .Nm
 will set this automatically.
index 149a55c829fd06c934b48241b68676561e5b5201..1c6a656923ab644b1d14901c37ceccd1a7bd59e1 100644 (file)
@@ -470,7 +470,7 @@ configure_interface1(struct interface *ifp)
 
        if (!(ifo->options & DHCPCD_IAID)) {
                /*
-                * An IAID is for identifying a unqiue interface within
+                * An IAID is for identifying an unique interface within
                 * the client. It is 4 bytes long. Working out a default
                 * value is problematic.
                 *
index b642ab30617b8eb62e8a9ec81a0168c8b940fc06..3c53a99ab4e78187ca644fead16fe03b6d01de63 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/if_vlan.h>
 #include <linux/filter.h>
 #include <linux/netlink.h>
+#include <linux/ethtool.h> /* for ipvlan detection */
 #include <linux/sockios.h>
 #include <linux/rtnetlink.h>
 
@@ -309,12 +310,55 @@ if_init(struct interface *ifp)
        return if_writepathuint(ifp->ctx, path, 1) == -1 ? -1 : 0;
 }
 
+/* Returns number of bytes written to driver, else 0 (error or indeterminate). */
+static size_t
+if_get_driver(struct interface *ifp, char *driver, const size_t driverlen)
+{
+       struct ethtool_drvinfo drvinfo = { .cmd = ETHTOOL_GDRVINFO };
+       struct ifreq ifr = { .ifr_data = (void *)&drvinfo };
+
+       strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+       if (ioctl(ifp->ctx->pf_inet_fd, SIOCETHTOOL, &ifr) != 0) {
+               logerr("%s: SIOCETHTOOL ifname=%s", __func__, ifp->name);
+               return 0; /* 0 means error or indeterminate driver name */
+       }
+       return strlcpy(driver, drvinfo.driver, driverlen);
+}
+
+static bool
+if_cmp_driver(struct interface *ifp, const char *driver)
+{
+       char ifdriver[FIELD_SIZEOF(struct ethtool_drvinfo, driver)];
+       size_t n = if_get_driver(ifp, ifdriver, sizeof(ifdriver));
+
+       if (n == 0) {
+               logerr("%s: if_get_driver ifname=%s", __func__, ifp->name);
+               return false;
+       }
+       if (strncmp(ifdriver, driver, n) == 0)
+               return true;
+       return false;
+}
+
+static bool
+if_ipvlan(struct interface *ifp)
+{
+       if (if_cmp_driver(ifp, "ipvlan"))
+               return true;
+       return false;
+}
+
 int
 if_conf(struct interface *ifp)
 {
        char path[sizeof(SYS_LAYER2) + IF_NAMESIZE];
        int n;
 
+       /* Set broadcast flag for ipvlan interfaces.
+        * XXX: move this out to dhcpcd if needed on other platforms. */
+       if (if_ipvlan(ifp))
+               ifp->options->options |= DHCPCD_BROADCAST;
+
        /* Some qeth setups require the use of the broadcast flag. */
        snprintf(path, sizeof(path), SYS_LAYER2, ifp->name);
        n = check_proc_int(ifp->ctx, path);