Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 22 Feb 2025 02:12:45 GMT
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 3b281d1421a7 - main - netinet: enforce broadcast mode for all-ones and all-zeroes destinations
Message-ID:  <202502220212.51M2CjUT099250@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=3b281d1421a78b588c5fc4182009ce62d8823d95

commit 3b281d1421a78b588c5fc4182009ce62d8823d95
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2025-02-22 02:11:00 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2025-02-22 02:11:00 +0000

    netinet: enforce broadcast mode for all-ones and all-zeroes destinations
    
    When a socket has SO_BROADCAST set and destination address is INADDR_ANY
    or INADDR_BROADCAST, the kernel shall pick up first broadcast capable
    interface and broadcast the packet out of it.  Since this API is not
    reliable on a machine with > 1 broadcast capable interfaces, all practical
    software seems to use IP_ONESBCAST or other mechanisms to send broadcasts.
    This has been broken at least since FreeBSD 6.0, see bug 99558.  Back then
    the problem was in the fact that in_broadcast() check was always done
    against the gateway address, not the destination address.  Later, with
    90cc51a1ab4be, a second problem piled on top - we aren't checking for
    INADDR_ANY and INADDR_BROADCAST at all.
    
    Better late than never, fix that by checking destination address.
    
    PR:                     99558
    Reviewed by:            markj
    Differential Revision:  https://reviews.freebsd.org/D49042
---
 sys/netinet/in.h        |  7 +++++++
 sys/netinet/ip_output.c | 15 ++++++++++-----
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 0ee4200017b5..fa710af7cd58 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -686,6 +686,13 @@ char	*inet_ntop(int, const void *, char *, socklen_t); /* in libkern */
 int	 inet_pton(int af, const char *, void *); /* in libkern */
 void	 in_ifdetach(struct ifnet *);
 
+static inline bool
+in_broadcast(struct in_addr in)
+{
+	return (in.s_addr == htonl(INADDR_BROADCAST) ||
+	    in.s_addr == htonl(INADDR_ANY));
+}
+
 #define	in_hosteq(s, t)	((s).s_addr == (t).s_addr)
 #define	in_nullhost(x)	((x).s_addr == INADDR_ANY)
 #define	in_allhosts(x)	((x).s_addr == htonl(INADDR_ALLHOSTS_GROUP))
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 1811becbf387..35aaf85d6a4e 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -449,7 +449,8 @@ again:
 		mtu = ifp->if_mtu;
 		ip->ip_ttl = 1;
 		isbroadcast = ifp->if_flags & IFF_BROADCAST ?
-		    in_ifaddr_broadcast(dst->sin_addr, ia) : 0;
+		    (in_broadcast(ip->ip_dst) ||
+		    in_ifaddr_broadcast(dst->sin_addr, ia)) : 0;
 		src = IA_SIN(ia)->sin_addr;
 	} else if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
 	    imo != NULL && imo->imo_multicast_ifp != NULL) {
@@ -502,8 +503,11 @@ again:
 			gw = &nh->gw_sa;
 		if (nh->nh_flags & NHF_HOST)
 			isbroadcast = (nh->nh_flags & NHF_BROADCAST);
-		else if ((ifp->if_flags & IFF_BROADCAST) && (gw->sa_family == AF_INET))
-			isbroadcast = in_ifaddr_broadcast(((const struct sockaddr_in *)gw)->sin_addr, ia);
+		else if ((ifp->if_flags & IFF_BROADCAST) &&
+		    (gw->sa_family == AF_INET))
+			isbroadcast = in_broadcast(ip->ip_dst) ||
+			    in_ifaddr_broadcast(
+			    ((const struct sockaddr_in *)gw)->sin_addr, ia);
 		else
 			isbroadcast = false;
 		mtu = nh->nh_mtu;
@@ -533,11 +537,12 @@ again:
 			gw = &nh->gw_sa;
 		ia = ifatoia(nh->nh_ifa);
 		src = IA_SIN(ia)->sin_addr;
-		isbroadcast = (((nh->nh_flags & (NHF_HOST | NHF_BROADCAST)) ==
+		isbroadcast = ((nh->nh_flags & (NHF_HOST | NHF_BROADCAST)) ==
 		    (NHF_HOST | NHF_BROADCAST)) ||
 		    ((ifp->if_flags & IFF_BROADCAST) &&
 		    (gw->sa_family == AF_INET) &&
-		    in_ifaddr_broadcast(((const struct sockaddr_in *)gw)->sin_addr, ia)));
+		    (in_broadcast(ip->ip_dst) || in_ifaddr_broadcast(
+		    ((const struct sockaddr_in *)gw)->sin_addr, ia)));
 	}
 
 	/* Catch a possible divide by zero later. */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202502220212.51M2CjUT099250>