git: 3b281d1421a7 - main - netinet: enforce broadcast mode for all-ones and all-zeroes destinations
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 22 Feb 2025 02:12:45 UTC
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. */