Skip to content

Commit 9c6347a

Browse files
committed
Linux: use broadcast flag with ipvlan interfaces by default.
Linux ipvlan interfaces share a MAC address with their siblinds 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. This chicken-and-egg problem is resolved with two changes: - Set 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.)
1 parent 01748b3 commit 9c6347a

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

src/common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@
6363
#define ROUNDUP4(a) (1 + (((a) - 1) | 3))
6464
#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
6565

66+
#ifndef FIELD_SIZEOF
67+
#define FIELD_SIZEOF(t, f) sizeof(((t *)0)->f)
68+
#endif
69+
6670
/* Some systems don't define timespec macros */
6771
#ifndef timespecclear
6872
#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)

src/dhcpcd.8.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ of a randomly generated number.
651651
.It Fl J , Fl Fl broadcast
652652
Instructs the DHCP server to broadcast replies back to the client.
653653
Normally this is only set for non-Ethernet interfaces,
654-
such as FireWire and InfiniBand.
654+
such as FireWire and InfiniBand, and on Linux-based systems for ipvlan as well.
655655
In most instances,
656656
.Nm
657657
will set this automatically.

src/dhcpcd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ configure_interface1(struct interface *ifp)
470470

471471
if (!(ifo->options & DHCPCD_IAID)) {
472472
/*
473-
* An IAID is for identifying a unqiue interface within
473+
* An IAID is for identifying an unique interface within
474474
* the client. It is 4 bytes long. Working out a default
475475
* value is problematic.
476476
*

src/if-linux.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/if_vlan.h>
4343
#include <linux/filter.h>
4444
#include <linux/netlink.h>
45+
#include <linux/ethtool.h> /* for ipvlan detection */
4546
#include <linux/sockios.h>
4647
#include <linux/rtnetlink.h>
4748

@@ -309,12 +310,55 @@ if_init(struct interface *ifp)
309310
return if_writepathuint(ifp->ctx, path, 1) == -1 ? -1 : 0;
310311
}
311312

313+
/* Returns number of bytes written to driver, else 0 (error or indeterminate). */
314+
static size_t
315+
if_get_driver(struct interface *ifp, char *driver, const size_t driverlen)
316+
{
317+
struct ethtool_drvinfo drvinfo = { .cmd = ETHTOOL_GDRVINFO };
318+
struct ifreq ifr = { .ifr_data = (void *)&drvinfo };
319+
320+
strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
321+
if (ioctl(ifp->ctx->pf_inet_fd, SIOCETHTOOL, &ifr) != 0) {
322+
logerr("%s: SIOCETHTOOL ifname=%s", __func__, ifp->name);
323+
return 0; /* 0 means error or indeterminate driver name */
324+
}
325+
return strlcpy(driver, drvinfo.driver, driverlen);
326+
}
327+
328+
static bool
329+
if_cmp_driver(struct interface *ifp, const char *driver)
330+
{
331+
char ifdriver[FIELD_SIZEOF(struct ethtool_drvinfo, driver)];
332+
size_t n = if_get_driver(ifp, ifdriver, sizeof(ifdriver));
333+
334+
if (n == 0) {
335+
logerr("%s: if_get_driver ifname=%s", __func__, ifp->name);
336+
return false;
337+
}
338+
if (strncmp(ifdriver, driver, n) == 0)
339+
return true;
340+
return false;
341+
}
342+
343+
static bool
344+
if_ipvlan(struct interface *ifp)
345+
{
346+
if (if_cmp_driver(ifp, "ipvlan"))
347+
return true;
348+
return false;
349+
}
350+
312351
int
313352
if_conf(struct interface *ifp)
314353
{
315354
char path[sizeof(SYS_LAYER2) + IF_NAMESIZE];
316355
int n;
317356

357+
/* Set broadcast flag for ipvlan interfaces.
358+
* XXX: move this out to dhcpcd if needed on other platforms. */
359+
if (if_ipvlan(ifp))
360+
ifp->options->options |= DHCPCD_BROADCAST;
361+
318362
/* Some qeth setups require the use of the broadcast flag. */
319363
snprintf(path, sizeof(path), SYS_LAYER2, ifp->name);
320364
n = check_proc_int(ifp->ctx, path);

0 commit comments

Comments
 (0)