Page MenuHomeFreeBSD

udp: make sendto(2) on unconnected UDP socket use public inpcb KPIs
Needs ReviewPublic

Authored by glebius on Mon, Feb 17, 11:38 PM.
Tags
None
Referenced Files
Unknown Object (File)
Tue, Feb 18, 1:02 AM
Unknown Object (File)
Tue, Feb 18, 1:01 AM
Subscribers

Details

Reviewers
markj
Group Reviewers
network
transport
Summary

UDP allows to sendto(2) on unconnected socket. The original BSD devise
was that such action would create a temporary (for the duration of the
syscall) connection between our inpcb and remote addr:port specified in
sockaddr 'to' of the syscall. This devise was broken in 2002 in
90162a4e87f0. For more motivation on the removal of the temporary
connection see email [1].

Since the removal of the true temporary connection the sendto(2) on
unconnected socket has the following side effects:

  1. After first sendto(2) the "unconnected" socket will receive datagrams destined to the selected port.
  2. All subsequent sendto(2) calls will use the same source port.

Effectively, such sendto(2) acts like a bind(2) to INADDR_ANY:0. Indeed,
if you do this:

s1 = socket(PF_INET, SOCK_DGRAM, 0);
s2 = socket(PF_INET, SOCK_DGRAM, 0);
sendto(s1, ..., &somedestination, ...);
bind(s2, &{ .sin_addr = INADDR_ANY, sin_port = 0 });

And then look into kgdb at resulting inpcbs, you would find them equal in
all means modulo bound to different anonymous ports.

What is even more interesting is that Linux kernel had picked up same
behavior, including that "unconnected" socket will receive datagrams. So
it seems that such behavior is now an undocumented standard, thus I
covered it in recently added tests/sys/netinet/udp_bindings.

Now, with the above knowledge at hand, why are we using
in_pcbconnect_setup() and in_pcbinshash(), which are supposed to be
private to in_pcb.c, to achieve the binding? Let's use public KPI
in_pcbbind() on the first sendto(2) and use in_pcbladdr() on all
sendto(2)s. Apart from finally hiding these two should be private
functions, we no longer acquire global INP_HASH_WLOCK() for every
sendto(2) on unconnected socket as well as remove a couple workarounds.

[1] https://mail-archive.FreeBSD.org/cgi/mid.cgi?200210141935.aa83883

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 62527
Build 59411: arc lint + arc unit

Event Timeline

Nice change.

sys/netinet/udp_usrreq.c
1314

Don't you want to reload laddr = inp->inp_laddr here?

sys/netinet/udp_usrreq.c
1314

That would probably be a meaningful change in a jail. And then, the call to in_pcbladdr() would not be needed. I will check.

Indeed, assigning laddr avoids call to in_pcbladdr() and jailed socket
passes the test.