changeset 4480:d3e01f5908b4 draft

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.
author Roy Marples <roy@marples.name>
date Thu, 25 Apr 2019 12:02:50 +0000
parents 7cd8c57f33a0
children 7848309c100c
files src/if-sun.c
diffstat 1 files changed, 52 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/if-sun.c	Wed Apr 24 15:21:41 2019 +0100
+++ b/src/if-sun.c	Thu Apr 25 12:02:50 2019 +0000
@@ -845,6 +845,21 @@
 	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 (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 @@
 
 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 (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 @@
 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 @@
 		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 @@
 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 @@
 	/* 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 @@
 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 @@
 		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;