Skip to content

Commit

Permalink
Linux: use broadcast flag with ipvlan interfaces by default.
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
ido committed Apr 25, 2021
1 parent 01748b3 commit 9c6347a
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
#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)
Expand Down
2 changes: 1 addition & 1 deletion src/dhcpcd.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/dhcpcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
44 changes: 44 additions & 0 deletions src/if-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 9c6347a

Please sign in to comment.