Connecting to yourself (and hogging a port number) - linux

We ran across a situation where an application apparently "half-connects" to itself through an system assigned port number and "gets stuck" on Linux (Centos 6.4 in particular).
The situation is as follows:
A (python) app is trying to connect to some service, let's say at IP address 192.168.1.201:8081. For some reason, the assigned outgoing port is 8081. The connect succeeds but no further activity occurs on the socket because it is not really connected but sort of half-way connected (My guess is that only half the handshake is completed and Linux is doing the rest in the background to improve parallelism). A read statement on the socket hangs waiting for the rest of the connect to complete.
Here is a simple C++ program that replicates the problem. Run it with the IP address of the host you are running on. It is peculiar because we are binding the connecting socket to a port (which is legal), and then connecting to the same address and port without an error message being generated, without a "listen," and with the read hanging.
./foo 192.168.1.201
Connected...going to read...'
ss -na
ESTAB 0 0 192.168.1.201:8081 192.168.1.201:8081
If you kill the program, the socket goes into Time-Wait:
TIME-WAIT 0 0 192.168.1.201:8081 192.168.1.201:8081
The question is: Can this happen with system assigned ports? Can you get into a state where somehow the outbound address/port ends up matching the destination address/port and the system deadlocks? That appears to be what we are seeing.
Thanks,
-- Mike
Program:
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
int ret;
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
struct sockaddr_in sa_loc;
if(argc != 2)
{
printf("\n Usage: %s <ip of server> \n",argv[0]);
return 1;
}
memset(recvBuff, '0',sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
memset(&sa_loc, 0, sizeof(struct sockaddr_in));
sa_loc.sin_family = AF_INET;
sa_loc.sin_port = htons(8081);
sa_loc.sin_addr.s_addr = inet_addr(argv[1]);
ret = bind(sockfd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr));
if (ret != 0) {
perror("bind");
exit(1);
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8081);
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\n inet_pton error occured\n");
return 1;
}
ret = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret != 0) {
perror("connect");
exit(1);
}
printf("Connected...going to read...\n"); fflush(stdout);
while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
recvBuff[n] = 0;
printf("%s", recvBuff); fflush(stdout);
}
if(n <= 0) {
perror("read");
exit(1);
}
return 0;
}

By binding the socket to a port and then connecting to the very same port, you established a kind of loopback connection of the socket to itself.
Can this happen with system assigned ports?
No, it can't, because connect(3) promises:
If the socket has not already been bound to a local address,
connect() shall bind it to an address which, unless the
socket's address family is AF_UNIX, is an unused local address.
This unused local address is never the same as the one a running server's socket is bound to. Plus, the bind() in your client wouldn't have succeeded if a server for that port ran already.
Can you get into a state where somehow the outbound address/port ends
up matching the destination address/port and the system deadlocks?
We can only get into the observed state if we explicitly program that way, by binding to the very same port we're then connecting to. Also, the system doesn't deadlock, only the application blocks, quite simply because it calls read(), waiting for data which is never written.

Related

Can I (IGMP) join a stream on two NICs and answer (IGMP) queries on both NICs in Linux?

I made a Linux application to receive multicast traffic. It works when I connect to one interface. When I connect to a stream, in Wireshark, I see an IGMP join, and when the switch sends IGMP queries, Linux replies with an IGMP report for the stream.
However, I need more bandwidth than my one interface can provide. To have more bandwidth, I have multiple interfaces on the same network. I therefore duplicated my code to have two interfaces connect to a stream. In that case, in Wireshark, I see an IGMP join on both interfaces, but when the switch sends IGMP queries, Linux only replies with an IGMP report on one interface. Therefore, the switch timeout occurs and I lose the stream on the interface that is not reporting.
Here is a reproducible example. It doesn't receive any data, but it is enough to see the problem happen in Wireshark:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for both sockets.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create first socket.
int firstSocketToUse;
if ((firstSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("First socket failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddressForFirstSocket;
reuseAddressForFirstSocket = 1;
if (setsockopt(firstSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForFirstSocket, sizeof(reuseAddressForFirstSocket) ) == -1 ) {
perror("Error setting first socket for address reuse");
exit(1);
}
// Bind first socket.
if (bind(firstSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("First bind failed");
exit(1);
}
// Join stream on first socket.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(firstSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1) {
perror("Error joining multicast group on Interface 1");
exit(1);
}
// Create second socket.
int secondSocketToUse;
if ((secondSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Second socket failed");
exit(1);
}
// Set second socket for address reuse.
int reuseAddressForSecondSocket;
reuseAddressForSecondSocket = 1;
if (setsockopt(secondSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForSecondSocket, sizeof(reuseAddressForSecondSocket) ) == -1 ) {
perror("Error setting second socket for address reuse");
exit(1);
}
// Bind second socket.
if (bind(secondSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("Second bind failed");
exit(1);
}
// Join stream on second socket.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(secondSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1) {
perror("Error joining multicast group on Interface 2");
exit(1);
}
// Wait forever.
while(1) {}
}
I saw a post on Stackoverflow suggesting to do this with only one socket, so I tried that, but the same issue occurs. Here is the code for that:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for the socket.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create socket.
int socketToUse;
if ((socketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket failed");
exit(1);
}
// Bind socket.
if (bind(socketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("bind failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddress;
reuseAddress = 1;
if (setsockopt(socketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddress, sizeof(int) ) == -1 ) {
perror("Error setting socket for address reuse");
exit(1);
}
// Join stream on first interface.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1)
{
perror("Error joining multicast group on first interface");
exit(1);
}
// Join stream on second interface.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1)
{
perror("Error joining multicast group on second interface");
exit(1);
}
// Wait forever.
while(1) {}
}
I've also tried to put the interfaces in promiscuous mode:
sudo ip link set interface1 promisc on
sudo ip link set interface2 promisc on
and add the noprefixroute option to each interface:
sudo ip addr change 25.25.40.134 dev interface1 noprefixroute
sudo ip addr change 25.25.40.116 dev interface2 noprefixroute
Both those things failed to solve my problem.
That being said, I found sources (1, 2) that might indicate that what I'm trying to do is impossible, though these sources seem somewhat old.
I was able to fix the issue by putting both ports on the switch to different vlans, though that is a clunky/awful solution that might not be acceptable.
Is connecting to multicast streams on the same network through two interfaces and configure the network to answer the IGMP queries on both interfaces possible?
It is explained as clear as day here:
https://access.redhat.com/solutions/53031
And less clearly here:
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
Considering a computer with 2 net interfaces, interfaceA and interfaceB.
Considering that Linux decides to use interfaceB to send packets to ip address X.
Considering a packet that is received on interfaceA from ip address X.
Linux will drop the packet.
Unless you run
sysctl net.ipv4.conf.all.rp_filter=2 in a terminal or add that line to /etc/sysctl.conf.
It enables receiving packets from an ip address on other interfaces than the one it uses to send packets to that ip address!

Select MAC address for binding to a specific bluetooth adapter

The following piece of code binds a Bluetooth socket. My expectation is that the call to bind() (or to listen()) with that MAC address as argument fails, because when using AF_INET sockets, I cannot bind() to an IP address that I don't have. This answer suggests that it's possible to select your adapter, but I cannot reproduce that in my experiments.
/* From https://people.csail.mit.edu/albert/bluez-intro/x502.html */
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
// loc_addr.rc_bdaddr = *BDADDR_ANY;
bdaddr_t my_bdaddr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
loc_addr.rc_bdaddr = my_bdaddr;
loc_addr.rc_channel = (uint8_t) 1;
{
int res = bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
char baddr[128];
ba2str(&loc_addr.rc_bdaddr, baddr);
fprintf(stderr, "bound to %s: %d\n", baddr, res);
}
// put socket into listening mode
{
int res = listen(s, 1);
fprintf(stderr, "Listening: %d\n", res);
}
// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
// close connection
close(client);
close(s);
return 0;
}
So I guess my question is: Is my expectation of the bind() having to fail wrong?
The main reason I am concerned with the MAC address is that I want another party to connect to me and send it some data. For the other party to establish a connection, it needs my MAC address and port. Now I can find my MAC address via the DBus API (or probably via some other means) but then I'm afraid that it's racy, e.g. I cannot be sure that the MAC address I queried is the same that I've bound to, because in the time between the query and the bind the user may have removed their Bluetooth adapter and inserted a new one. And before I jump through the hoops of subscribing to BlueZ DBus signals I thought I'd use the MAC for binding the socket.

Some TCP states not showing in the terminal

I have written a basic client server code to understand the TCP states.
Client code :
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(){
int clientSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
socklen_t addr_size;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
clientSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(7891);
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Connect the socket to the server using the address struct ----*/
addr_size = sizeof serverAddr;
connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
/*---- Read the message from the server into the buffer ----*/
recv(clientSocket, buffer, 1024, 0);
/*---- Print the received message ----*/
printf("Data received: %s",buffer);
return 0;
}
Server Code :
/****************** SERVER CODE ****************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(){
int welcomeSocket, newSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
welcomeSocket = socket(PF_INET, SOCK_STREAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(7891);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
if(listen(welcomeSocket,5)==0)
printf("Listening\n");
else
printf("Error\n");
addr_size = sizeof serverStorage;
newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
strcpy(buffer,"Hello World\n");
send(newSocket,buffer,13,0);
return 0;
}
I am running the command netstat -an | grep 7891, so at various points of time, I am getting the states ESTABLISHED, LISTENING, FINWAIT2, CLOSE_WAIT and TIME_WAIT.
How to get the other states like SYN_RECV, SYN_SENT, FINWAIT1, CLOSING and LAST ACK.
I have tried with various netstat options and ss options but to no vain.
You do not see SYN_RECV, SYN_SENT and other, because these stages are too short. For example, when after succesfull accept() on server side you have got ESTABLISHED, all previous states would be performed rapidly by TCP stack.
So, you are watching only long states of tcp connections.
You can achieve SYN_* states by simulating SYN-flood: use backlog parameter for listen(...,1) at server side and sleep before return 0. And try to launch several separate clients. As result you will get: 1 in ETSABLISHED and several in SYN_* states.
About finalize states. You should call shutdown() and close() and set sleep between them too. I recommend you to use fork() to make child process and use synchronizations primitives to understand TCP states

setsockopt on "accepted" fd on Linux

I have had a rather strange observation about behavior of setsockopt on Linux for SO_REUSEADDR. In one line: if I apply the sockopt to an fd returned by accept on a "listening socket" the socketoption is reflected on the port held by the listening socket.
Ok some code.
Server : Opens a socket, applies SO_REUSEADDR to be true. Accepts a connection and then applies SO_REUSEADDR to be false on the fd on the fd returned by accept.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
int main(void)
{
int s, len;
int sin_size;
int reuse = 1;
int ret;
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
my_addr.sin_port = htons(33235);
if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error\n");
return -1;
}
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if( bind(s, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0)
{
printf("Bind Error\n");
return -1;
}
listen(s, 6);
reuse = 0;
memset(&my_addr, 0, sizeof(my_addr));
while(1) {
ret = accept(s, (struct sockaddr*)&my_addr, &len);
if (ret<0) {
printf("Accept failed\n");
} else {
printf("Accepted a client setting reuse add to 0\n");
setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
}
}
printf("Server exiting\n");
return 0;
}
Client : Client connects to the server, and doesn't do anything after that ensuring that the server socket stays in TIME_WAIT state.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
int main(void)
{
int s, len;
int sin_size;
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
my_addr.sin_port = htons(33235);
if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error\n");
return -1;
}
if (!connect(s,(struct sockaddr*)&my_addr, sizeof(struct sockaddr)))
{
printf("Client Connected successfully\n");
}
else
{
printf("%s\n",strerror(errno));
}
while(1) sleep(1);
return 0;
}
Steps that I do reproduce the issue.
Run server.
Connect client.
Kill and restart server. The server fails with Bind Failure
I tested this on mac os. And the bind didn't fail. I have digged up all Posix specifications and none of them say that this code is undefined.
Question:
Can someone with more experience on this share their understanding of the issue?
One way to think about it is that SO_REUSEADDR determines if you can have another socket bound to that same address. It's a property of any socket (listen or connection), but very commonly inherited from listen via accept. In linux it's mapped to the struct sock "sk_reuse" flag.
If you clear this flag on a FD you "accepted" then from that point on the IP/Port pair is considered busy-and-non-reusable. The SO_REUSEADDR flag on the listen socket does not change, but the flag on the accepted socket affects bind logic. You could probably check this with getsockopt.
If you want to know more you can try to read the inet_csk_get_port function: http://lxr.free-electrons.com/source/net/ipv4/inet_connection_sock.c#L100. This is where the actual "binding" takes place.

How getservbyname can get the server port information if it is run on a client machine?

The following client program trys to connect to a server and finds the current time and date on that server.
/* Start with the usual includes and declarations. */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *host;
int sockfd;
int len, result;
struct sockaddr_in address;
struct hostent *hostinfo;
struct servent *servinfo;
char buffer[128];
if(argc == 1)
host = "localhost";
else
host = argv[1];
/* Find the host address and report an error if none is found. */
hostinfo = gethostbyname(host);
if(!hostinfo) {
fprintf(stderr, "no host: %s\n", host);
exit(1);
}
/* Check that the daytime service exists on the host. */
servinfo = getservbyname("daytime", "tcp");
if(!servinfo) {
fprintf(stderr,"no daytime service\n");
exit(1);
}
printf("daytime port is %d\n", ntohs(servinfo -> s_port));
/* Create a socket. */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
/* Construct the address for use with connect... */
address.sin_family = AF_INET;
address.sin_port = servinfo -> s_port;
address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list;
len = sizeof(address);
/* ...then connect and get the information. */
result = connect(sockfd, (struct sockaddr *)&address, len);
if(result == -1) {
perror("oops: getdate");
exit(1);
}
result = read(sockfd, buffer, sizeof(buffer));
buffer[result] = '\0';
printf("read %d bytes: %s", result, buffer);
close(sockfd);
exit(0);
}
Question:
We run the above program on a client machine, how the function getservbyname can get the
server information without a reference to the server machine in the parameter list?
It examines /etc/services for an entry with the given service name and protocol.
$ grep "^daytime\s.*/tcp" /etc/services
daytime 13/tcp
getservbyname simply looks in /etc/services to find the "daytime" service using the "tcp" protocol.
It's just a convenience, to save you from parsing that file.
Edit (clarification)
Each of these protocols has a friendly name ("daytime", "http", etc) and a useful name (the port number - 13, 80 etc). /etc/services holds this mapping, nothing more.

Resources