by dixie on Date unknown
Tagged as: network.
TCP state machine allows that TCP client establishes connection using connect()
to itself. Look on diagram and focus on Simultaneous Open part.
Following conditions are needed for the loop:
Preconditions don’t automatically guarantee the loop situation. But they are needed. Loop happens when OS bind the TCP port to client attempting to connect on the same. It can happen only for ephemeral ports.
The correctly designed TCP client/server application should not use the ephemeral ports for service side listener. Otherwise there is a chance it will establish unwanted connection to itself - loop.
In general it depends on various conditions like how OS assign ephemeral ports and how TCP client constructs sockets, but probability increases when:
/*
* 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 ... */
For the details please read article describing them, basically on linux they can be displayed using:
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
Do not use for TCP lister port the value from that range.
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;
}
Three commands outputs:
lsof
output for port 33333 in the other terminal$ 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)