summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2019-04-25 12:02:50 +0000
committerRoy Marples <roy@marples.name>2019-04-25 12:02:50 +0000
commitb995aecb8dcfedae41c81a637e97411b179203ff (patch)
treee41e531ed2aef1e97a714ceeb2017191d78990c4
parentffb6d59b633fbad8c53f403e4d6640d53aca2070 (diff)
downloaddhcpcd-b995aecb8dcfedae41c81a637e97411b179203ff.tar.xz
sun: Detect correct broadcast address as RTM_NEWADDR lies
I suspect this is because the broadcast address needs to be set after the address, but RTM_NEWADDR is triggered from adding the address. While here, actually set the broadcast address requested for IPv4 and simplify the code.
-rw-r--r--src/if-sun.c81
1 files changed, 52 insertions, 29 deletions
diff --git a/src/if-sun.c b/src/if-sun.c
index 191e0e68..9ab81c48 100644
--- a/src/if-sun.c
+++ b/src/if-sun.c
@@ -845,6 +845,21 @@ if_getalias(struct interface *ifp, const struct sockaddr *sa, char *alias)
return found;
}
+static int
+if_getbrdaddr(struct dhcpcd_ctx *ctx, const char *ifname, struct in_addr *brd)
+{
+ struct lifreq lifr = { 0 };
+ int r;
+
+ memset(&lifr, 0, sizeof(lifr));
+ strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+ errno = 0;
+ r = ioctl(ctx->pf_inet_fd, SIOCGLIFBRDADDR, &lifr, sizeof(lifr));
+ if (r != -1)
+ COPYOUT(*brd, (struct sockaddr *)&lifr.lifr_broadaddr);
+ return r;
+}
+
static void
if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
{
@@ -908,6 +923,11 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
if (ia == NULL)
return;
strlcpy(ifalias, ia->alias, sizeof(ifalias));
+ } else if (bcast.s_addr == INADDR_ANY) {
+ /* Work around a bug where broadcast
+ * address is not correctly reported. */
+ if (if_getbrdaddr(ctx, ifalias, &bcast) == -1)
+ return;
}
flags = if_addrflags(ifp, &addr, ifalias);
if (ifam->ifam_type == RTM_DELADDR) {
@@ -1035,7 +1055,8 @@ if_octetstr(char *buf, const Octet_t *o, ssize_t len)
static int
if_addaddr(int fd, const char *ifname,
- struct sockaddr_storage *addr, struct sockaddr_storage *mask)
+ struct sockaddr_storage *addr, struct sockaddr_storage *mask,
+ struct sockaddr_storage *brd)
{
struct lifreq lifr;
@@ -1052,6 +1073,13 @@ if_addaddr(int fd, const char *ifname,
if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1)
return -1;
+ /* Then assign the broadcast address. */
+ if (brd != NULL) {
+ lifr.lifr_broadaddr = *brd;
+ if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1)
+ return -1;
+ }
+
/* Now bring it up. */
if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
return -1;
@@ -1205,15 +1233,11 @@ out:
static int
if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
{
- struct sockaddr_storage addr, mask;
+ struct sockaddr_storage addr = { .ss_family = af };
int fd;
/* For the time being, don't unplumb the interface, just
* set the address to zero. */
- memset(&addr, 0, sizeof(addr));
- addr.ss_family = af;
- memset(&mask, 0, sizeof(mask));
- mask.ss_family = af;
switch (af) {
#ifdef INET
case AF_INET:
@@ -1234,7 +1258,8 @@ if_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)
errno = EAFNOSUPPORT;
return -1;
}
- return if_addaddr(fd, ifname, &addr, &mask);
+ return if_addaddr(fd, ifname, &addr, &addr,
+ af == AF_INET ? &addr : NULL);
}
static int
@@ -1489,8 +1514,10 @@ bpf_send(const struct interface *ifp, __unused int fd, uint16_t protocol,
int
if_address(unsigned char cmd, const struct ipv4_addr *ia)
{
- struct sockaddr_storage ss_addr, ss_mask;
- struct sockaddr_in *sin_addr, *sin_mask;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_storage ss;
+ } addr, mask, brd;
/* Either remove the alias or ensure it exists. */
if (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1)
@@ -1507,14 +1534,11 @@ if_address(unsigned char cmd, const struct ipv4_addr *ia)
/* We need to update the index now */
ia->iface->index = if_nametoindex(ia->alias);
- sin_addr = (struct sockaddr_in *)&ss_addr;
- sin_addr->sin_family = AF_INET;
- sin_addr->sin_addr = ia->addr;
- sin_mask = (struct sockaddr_in *)&ss_mask;
- sin_mask->sin_family = AF_INET;
- sin_mask->sin_addr = ia->mask;
- return if_addaddr(ia->iface->ctx->pf_inet_fd,
- ia->alias, &ss_addr, &ss_mask);
+ sa_in_init(&addr.sa, &ia->addr);
+ sa_in_init(&mask.sa, &ia->mask);
+ sa_in_init(&brd.sa, &ia->brd);
+ return if_addaddr(ia->iface->ctx->pf_inet_fd, ia->alias,
+ &addr.ss, &mask.ss, &brd.ss);
}
int
@@ -1541,9 +1565,12 @@ if_addrflags(const struct interface *ifp, const struct in_addr *addr,
int
if_address6(unsigned char cmd, const struct ipv6_addr *ia)
{
- struct sockaddr_storage ss_addr, ss_mask;
- struct sockaddr_in6 *sin6_addr, *sin6_mask;
- struct priv *priv;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_storage ss;
+ } addr, mask;
+ const struct priv *priv;
int r;
/* Either remove the alias or ensure it exists. */
@@ -1558,15 +1585,11 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia)
return -1;
}
- priv = (struct priv *)ia->iface->ctx->priv;
- sin6_addr = (struct sockaddr_in6 *)&ss_addr;
- sin6_addr->sin6_family = AF_INET6;
- sin6_addr->sin6_addr = ia->addr;
- sin6_mask = (struct sockaddr_in6 *)&ss_mask;
- sin6_mask->sin6_family = AF_INET6;
- ipv6_mask(&sin6_mask->sin6_addr, ia->prefix_len);
- r = if_addaddr(priv->pf_inet6_fd,
- ia->alias, &ss_addr, &ss_mask);
+ sa_in6_init(&addr.sa, &ia->addr);
+ mask.sin6.sin6_family = AF_INET6;
+ ipv6_mask(&mask.sin6.sin6_addr, ia->prefix_len);
+ priv = (const struct priv *)ia->iface->ctx->priv;
+ r = if_addaddr(priv->pf_inet6_fd, ia->alias, &addr.ss, &mask.ss, NULL);
if (r == -1 && errno == EEXIST)
return 0;
return r;