2

While editing some firewall rules on a machine that has an OpenVPN client running, I was trying to determine its remote port, as I usually do, using ss.

# ss -anup | grep openvpn
UNCONN6528   0    0.0.0.0:52012        0.0.0.0:*     users:(("openvpn",pid=333,fd=3))

It came up empty.

From config, I can see that the client must be connected to 10.0.0.5:389 over UDP. I verified this in /proc:

# cat /proc/net/nf_conntrack | grep udp | grep 389
ipv4     2 udp      17 175 src=10.0.7.8 dst=10.0.0.5 sport=52012 dport=389 src=10.0.0.5 dst=10.0.7.8 sport=389 dport=52012 [ASSURED] mark=0 zone=0 use=2

Both these commands were run in quick succession after doing an outbound connection and verifying it to be working, curl ipinfo.io having returned the expected result.

I tried a normal UDP connection with netcat (nc -l -u 12345 on server and nc -u 10.0.0.5 12345 on the client) to see the output of ss on the client:

# ss -anup | grep nc
ESTAB      0      0      10.0.7.8:52051              10.0.0.5:12345               users:(("nc",pid=2002,fd=3))

Why is the ss output for OpenVPN in UNCONN state and not the expected ESTAB state, as it is in the simple netcat case?


Environment:

# uname -a
Linux tank 4.16.3-1-ARCH #1 SMP PREEMPT Thu Apr 19 09:17:56 UTC 2018 x86_64 GNU/Linux

# pacman -Qo /usr/bin/ss
/usr/bin/ss is owned by iproute2 4.16.0-1
2

TL;DR: you can use UDP sockets in connected mode or stay unconnected, that's an implementation choice depending among other factors on simplicity or scalability. This won't change the content of the packets on the wire or conntrack whatever choice is made.

netcat uses bind(2) to the chosen port, uses only once recvfrom(2) with option MSG_PEEK to not even consume the data, retrieves the source, then uses connect(2) to this source changing the state of the socket to ESTAB, and can now continue with simple read(2) and write(2) calls.

Other applications (eg: socat UDP-RECVFROM:7777,fork - instead of socat UDP-LISTEN:7777 -, and obviously openvpn) just never connect(2) to the source and thus stay in UNCONN state. They'll only use recvfrom(2) and will emit data using sendto(2).

This difference of use is partly explained in recv(2) and send(2):

The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write(2) is the presence of flags. With a zero flags argument, send() is equivalent to write(2).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.