TCP Loop Connect

[Go back to Index]

TCP Loop Connect

TCP state machine diagram allows that TCP client can establish connection with connect() to itself. Focus on Simultaneous Open on that diagram.

Mandatory conditions

Following conditions are mandatory on TCP Client for possibility of such case:

  1. Connection is established from HOST to HOST on the same IP
  2. Destination TCP Port has to be from range of ephemeral ports
  3. TCP Server application may not be running (e.g. crashed and not bound on listen port)

Probability

Above two rules doesn't assure of such situation, it is necessary that ephemeral port of TCP Client is actually that port which connects to.

Valid written TCP application should not use the ports within that range, because there is chance it MAY establish unwanted connection to itself.

Depends on OS ephemeral port assigment policy and how TCP client constructs sockets, but probability increases:

  1. If the client create for each connection new socket with new ephemeral port (done usually in some high-level languages by programmers)
/* 
* Try establish connection with 127.0.0.1:33333
* THIS IS BAD EXAMPLE because it behaves like infinite loop at various
* conditions
*/

do {
socket = getSockFromSystem(); /* it may use always different ephemeral port */
connectTo(socket, 127.0.0.1, 33333); /* it may connect to itself if ephemeral port == 33333 */
} while(isNotConnected(socket));

/* ... continue with established connection ... */

Ephemeral ports

For the details read article describing them, basically on linux they can be displayed using:

$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768   61000

It means that I should not use as TCP Listen port on given machine any port from this range.

Example TCP Client

Compile as gcc -o tcploop tcploop.c

/* tcploop.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>

int main(int argc, char *argv[])
{
int port = 0;
int sckfd = 0;
int opt = 1;
struct sockaddr_in *remote;
char *ip = "127.0.0.1";
int rc;
long n = 0;

if(argc != 2){
printf("Usage: %s <listen port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);

/* Set to Localhost:Port */
remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
remote->sin_family = AF_INET;
assert(inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr))) > 0);
remote->sin_port = htons(port);

/* create socket */
assert((sckfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0);
assert(setsockopt(sckfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) >= 0);

printf("Trying to connect..."); fflush(stdout);
while(1) {
n++;
rc = connect(sckfd, (struct sockaddr *)remote, sizeof(struct sockaddr));
if(rc < 0){
if(n % 1000 == 0) { printf("."); fflush(stdout); }
continue;
}
else {
printf("Connected after %ld tries\n", n);
break;
}
}
/* Wait for Enter */
printf("Press Enter to Continue...\n");
getchar();
close(sckfd);
return 0;
}

Example output

Here are three commands output:

  1. First is ephemeral port range discovery
  2. Invocation of tcploop with port from that range (33333)
  3. In another terminal while tcploop waits for Enter lsof output for port 33333
$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768   61000

$ ./tcploop 33333
Trying to connect..............Connected after 11263 tries
Press Enter to Continue...

$ lsof -n | grep 33333
COMMAND     PID  USER   FD      TYPE     DEVICE  SIZE/OFF       NODE NAME
tcploop    3440 dixie    3u     IPv4     163396       0t0        TCP 127.0.0.1:33333->127.0.0.1:33333 (ESTABLISHED)