Centos – TCP source port incremented by 2, always even for curl / wget


When using curl and wget, when source port is not set manually (like with –local-port in curl), the source TCP port is always even, and will increment by 2, instead of 1.

EG: in tcpdump when I make a connection I see source port 45080 used, and then the next connection will use 45082, rather than 45801. Using local-port I can force it to use odd ports, and tcpdump confirms these are succesfully being used.

This is causing me problems in a network test bed, and I can't for the life of me figure out what is controlling the implicit tcp port selection. I can change the range, but I cant change the "incrementation".

Using centos7 and "3.10.0-514.el7.x86_64" kernel.

in tcpdump I see the same behavior using wget and curl, which leads me to believe this is not a curl specific problem, but rather whatever under the hood mechanism is being used to select ports.

Also, if I I see curl using port 45080 for example, I know the next port it will use is 45082. If I force a –local-port of 50000, then curl once more without the –local-port, it will be 45082, like a separate counter is increment it, that is not affected by whatever the "last" used port was.

Alternatively, if do the same thing as the above step, but instead of force port 50000, I force it to use 45082, the port it was presumebly going to pick, then i use curl again without forcing a local port, it will pick 45083, then if again it will pick 45084, then 45086…

The only other way I could get it to naturally pick an odd one is if I limited the range to an odd number of ports.

is there a sys call, or some kind of kernel operation for picking source ports, and is there a way to change it?


Best Answer

I believe I was able to prove that the kernel itself is increment the ports this way.

Strace showed both wget and curl calling connect() without calling bind() to explicitly set the source port.

I wrote a python script to simulate the same thing, and it seems the 3.1 kernel increments by 2, and the 2.6 kernel does not.

Interestingly, this is only true when using connect(), if you bind on port 0, thus letting the kernel pick the next available port, it is pretty much random.

My script:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('', 80))
addr = s.getsockname()
print addr[1]


$ python port_test.py
$ python port_test.py
$ python port_test.py