Bug 39

Summary: Spliced TCP connections will present incorrect peer port to listeners in pasta
Product: passt Reporter: David Gibson <dgibson>
Component: TCPAssignee: nobody
Status: RESOLVED INVALID    
Severity: minor    
Priority: Normal    
Version: unspecified   
Hardware: All   
OS: Linux   

Description David Gibson 2022-11-17 05:00:04 UTC
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.
Comment 1 David Gibson 2023-11-13 04:50:55 UTC
On consideration, preserving the peer port number really isn't supported by the NAT model we have in mind.  Rather it's merely a coincidence that the "tap" case does preserve the number (for now).

Closing.