Re: [PATCH] man/man7/ip.7: don't say that bind(INADDR_BROADCAST) is the same as bind(INADDR_ANY)
From: наб <hidden>
Date: 2025-12-01 14:09:54
Hi! On Mon, Dec 01, 2025 at 11:04:15AM +0100, Alejandro Colomar wrote:
On Sun, Nov 30, 2025 at 05:52:16PM +0100, наб wrote:quoted
Because it's patently not true: a program like #include <netinet/in.h> #include <netinet/udp.h> #include <sys/socket.h> int main() { int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); bind(sock, (struct sockaddr *)&(struct sockaddr_in){.sin_family = AF_INET, .sin_port = htons(123), .sin_addr = INADDR_ANY}, sizeof(struct sockaddr_in));Minor quality-of-life comment: glibc accepts sockaddr_in without a cast:
Only with -D_GNU_SOURCE which is not the default on clang trunk:
$ make a
cc -fdebug-default-version=3 a.c -o a
a.c:8:51: warning: incompatible pointer types passing 'struct sockaddr_in *' to parameter of type 'const struct sockaddr *' [-Wincompatible-pointer-types]
8 | bind(sock, /*(struct sockaddr *)*/&(struct sockaddr_in){.sin_family = AF_INET, .sin_port = htons(123), .sin_addr = INADDR_ANY}, sizeof(struct sockaddr_in));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/x86_64-linux-gnu/sys/socket.h:112:49: note: passing argument to parameter '__addr' here
112 | extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
| ^
1 warning generated.
quoted
uint8_t buf[512]; recv(sock, buf, sizeof(buf), 0); } will receive an NTP broadcast, but if you s/INADDR_ANY/INADDR_BROADCAST/ it won't.Would you be able to compile the two programs (with _ANY and _BROADCAST) and run some shell session that shows that one receives it and the other doesn't? Only if it would be easy for you. Otherwise, I'll trust the below.
Hard to prove a negative, but they strace to:
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
recvfrom(3, "%\4\6\347\0\0\3\35\0\0\1\232\300V\16C\354\330\22`\365s\216y\0\0\0\0\0\0\0\0"..., 512, 0, NULL, NULL) = 48
exit_group(0) = ?
+++ exited with 0 +++
vs
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("255.255.255.255")}, 16) = 0
recvfrom(3, # this never resolves
quoted
Quoth POSIX.1-2024: 11252 The <netinet/in.h> header shall define the following symbolic constant for use as a destination 11253 address in the structures passed to connect( ), sendmsg( ), and sendto( ): 11254 INADDR_BROADCAST IPv4 broadcast address. (this is one of two mentions of the symbol, the other is a RATIONALE for why no byte ordering is specified). Linux calls it /* Address to send to all hosts. */ #define INADDR_BROADCAST ((unsigned long int) 0xffffffff) and AFAICT never uses it in the receive path (except in RDS to reject bind(2)). Fixes: commit fea681dafb136 ("Import of man-pages 1.70")Let's be a bit more specific: Fixes: fea681dafb13 (2004-11-03, 2004-11-03; "Import of man-pages 1.70") Fixes: 3c5f99be9759 (1999-11-28, 2022-12-19; "man-pages 1.28") Since we have the prehistory branch which has one commit per pre-1.70 release.
(conveniently, not blameable from master!) Blames to commit 3c5f99be97598106d12ea357b05a712a0b44c5e3 Author: Krónos <Krónos@Sāturnus> Date: Sun Nov 28 05:16:00 1999 +0100 man-pages 1.28 Link: <https://www.win.tue.nl/~aeb/ftpdocs/linux-local/manpages.archive/> Signed-off-by: Alejandro Colomar [off-list ref] which adds it alongside the other INADDR_*s, noting only +Differences from version 1.27: + ip.7 iso_8859-2.7 netdevice.7 packet.7 signal.7 socket.7 unix.7 + are new or have been updated. + Typographical or grammatical errors have been corrected in several other places.
quoted
--- a/man/man7/ip.7 +++ b/man/man7/ip.7@@ -199,11 +199,6 @@ .SS Special and reserved addresses means any address for socket binding; .TP .BR INADDR_BROADCAST " (255.255.255.255)" -has the same effect on -.BR bind (2) -as -.B INADDR_ANY -for historical reasons.Was this ever true? Has the behavior ever changed? It would be interesting to know if this documentation is just bogus, or if we should document some historic details.
No clue but I'd expect POSIX to mention this if it were real outside Linux.
And I don't think this was real on Linux because it wasn't real in Linux 1.2.13
(where bind(BROADCAST) actually fails instead of being a no-op):
$ cat a.BROADCAST.c
#include <netinet/in.h>
#include <netinet/udp.h>
#include <sys/socket.h>
int main() {
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int true = 1;
struct sockaddr_in addr;
char buf[512];
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(true));
addr.sin_family = AF_INET;
addr.sin_port = htons(123);
addr.sin_addr.s_addr = INADDR_BROADCAST;
bind(sock, &addr, sizeof(addr));
recv(sock, buf, sizeof(buf), 0);
}
$ diff a.*c
12c12
< addr.sin_addr.s_addr = INADDR_ANY;
---
> addr.sin_addr.s_addr = INADDR_BROADCAST;
$ cc -I /usr/src/kernel-headers-1.2.13/include/ a.BROADCAST.c -o a.BROADCAST
a.BROADCAST.c: In function `main':
a.BROADCAST.c:13: warning: passing arg 2 of `bind' from incompatible pointer type
$ cc -I /usr/src/kernel-headers-1.2.13/include/ a.ANY.c -o a.ANY
a.ANY.c: In function `main':
a.ANY.c:13: warning: passing arg 2 of `bind' from incompatible pointer type
$ dpkg -l includes libc gcc
Desired=Unknown/Install/Remove/Purge
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name Version Rev Description
+++-============-=========-====-===============================================
ii includes 1.2.13 4 Linux kernel headers, if you aren't installing
iU libc 4.6.27 6 The Linux C library.
iU gcc 2.6.3 4 The GNU C compiler.
$ uname -a
Linux 0.93R6 1.2.13 #2 Wed Oct 4 17:35:14 EST 1995 i686
# strace ./a.ANY
uselib("/lib/ld.so") = 0
getuid() = 0
geteuid() = 0
getgid() = 0
getegid() = 0
stat("/etc/ld.so.cache", {st_mode=S_IFREG|0644, st_size=455, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
mmap(0, 455, PROT_READ, MAP_SHARED, 3, 0) = 0x50000000
close(3) = 0
uselib("/lib/libc.so.4.6.27") = -1 ENOENT (No such file or directory)
uselib("/usr/lib/libc.so.4") = -1 ENOENT (No such file or directory)
uselib("/lib/libc.so.4") = 0
munmap(0x50000000, 455) = 0
munmap(0x62f00000, 20480) = 0
brk(0) = 0x3000
socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sin_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
recv(3,
# strace ./a.BROADCAST
uselib("/lib/ld.so") = 0
getuid() = 0
geteuid() = 0
getgid() = 0
getegid() = 0
stat("/etc/ld.so.cache", {st_mode=S_IFREG|0644, st_size=455, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
mmap(0, 455, PROT_READ, MAP_SHARED, 3, 0) = 0x50000000
close(3) = 0
uselib("/lib/libc.so.4.6.27") = -1 ENOENT (No such file or directory)
uselib("/usr/lib/libc.so.4") = -1 ENOENT (No such file or directory)
uselib("/lib/libc.so.4") = 0
munmap(0x50000000, 455) = 0
munmap(0x62f00000, 20480) = 0
brk(0) = 0x3000
socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sin_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("255.255.255.255")}, 16) = -1 EADDRNOTAVAIL (Cannot assign requested address)
recv(3,
(I made a program that does sendto(ADDR_BROADCAST) and none of them
caught it, but I may well be holding it wrong and/or have my VM
misconfigured.)
But there's lots of room for history between 1995 and 1999.
(If you don't know, let's assume it was bogus; we can always come back to this in the future, if we learn about it.)
Best,
Attachments
- signature.asc [application/pgp-signature] 833 bytes