Search code examples
socketsudpudpclient

UDP: Why can't you `bind` after a `connect`?


This works fine:

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(4, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("1.1.1.1")}, 16) = 0

And we can observe the proper binding to local and remote:

$ sudo lsof -Pni :4444
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
udpdup  2815 nhed    4u  IPv4 126724      0t0  UDP 10.0.2.15:4444->1.1.1.1:* 

But putting the connect first, get an error on the bind, why?

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("1.1.1.1")}, 16) = 0
bind(4, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = -1 EINVAL (Invalid argument)





Why do I care you ask?

I thought that received packets are matched against a 5-element tuple (proto, loc addr, loc port, rem addr, rem port). Where some of the 5 can be ANY (a wildcard), indicated numerically as 0 or 0.0.0.0 in struct sockaddr_in and often represented often in netstat, lsof, etc. The above lsof output shows 4/5 elements specified, remote port left as wildcard)

I could not remember if multiple sockets should exists where some had the wildcard peer addr and others could be explicit, sending traffic to the right port based on a best match.

I wanted to experiment and check that when one app has bound a port without specifying the sender address, that another app could connect & bind the peer, to form a more complete tuple. [note that the first app does not specify SO_REUSEADDR]

p.s. could someone with enough points please create a udp-connect tag? (and even a udp-bind)


Solution

  • connect(2) implicitly binds your local port, so subsequent explicit bind(2) fails.

    Edit 0:

    You can bind multiple sockets to the same local port, but all the bindings following the first one must be more specific. Say, first socket is bound to INADDR_ANY, and others are to specific addresses of existing network interfaces. Note, that all the sockets must be set with SO_REUSEADDR.

    Then there's multicast, which allows you to create completely duplicate socket bindings, but only to a specific class of addresses, and you have to do a bit more work to set it up.

    It also seems to me you are confusing this with TCP listening vs. connected sockets.

    Edit 1:

    connect(2) also fixes socket local address according to the route to destination.