Thread (5 messages) 5 messages, 2 authors, 2025-12-01

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

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help