A server listening on TCP can obtain the socket address of its peer - both the address and the port - either from accept() or with getpeername(). For example with the attached foo.sh script, we can do this: ## Server side $ socat -u TCP4-LISTEN:9999 EXEC:/home/dwg/src/passt/foo.sh Peer address: 192.168.17.36 Peer port: 43424 Data: hello ## Client side $ echo hello | strace -e getsockname socat -u STDIN TCP4:192.168.17.36:9999 getsockname(5, {sa_family=AF_INET, sin_port=htons(43424), sin_addr=inet_addr("192.168.17.36")}, [112 => 16]) = 0 +++ exited with 0 +++ Note that the source address and port reported by the client with getsockname() is the same as the peer address and port reported on the server. If the server is running under pasta, the address is translated to the gateway address as expected, but the port number is still preserved: ## Server $ ./pasta -t 9999 --config-net # socat -u TCP4-LISTEN:9999 EXEC:/home/dwg/src/passt/foo.sh Peer address: 192.168.17.1 Peer port: 55922 Data: hello ## Client $ echo hello | strace -e getsockname socat -u STDIN TCP4:192.168.17.36:9999 getsockname(5, {sa_family=AF_INET, sin_port=htons(55922), sin_addr=inet_addr("192.168.17.36")}, [112 => 16]) = 0 +++ exited with 0 +++ However, if localhost is used instead of a "real" IP address, the port number is no longer preserved: ## Server $ ./pasta -t 9999 --config-net # socat -u TCP4-LISTEN:9999 EXEC:/home/dwg/src/passt/foo.sh Peer address: 127.0.0.1 Peer port: 41874 Data: hello ## Client $ echo hello | strace -e getsockname socat -u STDIN TCP4:localhost:9999 getsockname(5, {sa_family=AF_NETLINK, nl_pid=3885151, nl_groups=00000000}, [12]) = 0 getsockname(5, {sa_family=AF_INET, sin_port=htons(38718), sin_addr=inet_addr("127.0.0.1")}, [28 => 16]) = 0 getsockname(5, {sa_family=AF_INET, sin_port=htons(41860), sin_addr=inet_addr("127.0.0.1")}, [112 => 16]) = 0 +++ exited with 0 +++ This is because coming from localhost uses a spliced socket which has its own source port address different from the originating socket.