The branch main has been updated by glebius:

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

commit 3b281d1421a78b588c5fc4182009ce62d8823d95
Author:     Gleb Smirnoff <gleb...@freebsd.org>
AuthorDate: 2025-02-22 02:11:00 +0000
Commit:     Gleb Smirnoff <gleb...@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. */

Reply via email to