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.
Related
I was writing a simple socket program for a server, where I got a hang in listen() call. Surprisingly, this piece of code hangs :
if((res = listen(sockfd, 5)) == -1)
{
perror("Error in listening over socket");
exit(1);
}
How come this is possible? Here's my full code for reference :
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define MYPORT 7891
int main()
{
int sockfd, newfd, res;
struct sockaddr_in my_addr, their_addr;
socklen_t their_addr_size;
char msg[100] = {'\0'};
/* open a socket for the server */
if((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1)
{
perror("Error opening socket");
exit(1);
}
printf("Socket opened successfully\n");
/* specify the interface details, where the server should listen for incoming messages. It is set by bind */
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY; /* listen on every interface, eth0, wlan, whatever f**kin place */
memset(&(my_addr.sin_zero),0,8);
if((res = bind(sockfd, (struct sockaddr *)&(my_addr), sizeof(struct sockaddr_in))) == -1)
{
perror("Error while bind()");
exit(1);
}
printf("Bind() is successfull\n");
/* listen on the socket, setting the waiting queue size to max 5 connections. Other connections will get ECONNREFUSED error */
if((res = listen(sockfd, 5)) == -1)
{
perror("Error in listening over socket");
exit(1);
}
// if(listen(sockfd,5)==0)
// printf("Listening\n");
// else
// printf("Error\n");
printf("Listening....");
/* accept incoming request */
their_addr_size = sizeof(struct sockaddr_in);
if((newfd = accept(sockfd, (struct sockaddr *)&their_addr, &their_addr_size)) == -1)
{
perror("Error accepting connection");
exit(1);
}
/* write data */
printf("Enter the data to be sent\n");
while(1)
{
scanf("%s",msg);
write(newfd, msg, strlen(msg));
}
/* though it never comes here due to infinite while loop */
close(newfd);
close(sockfd);
return 0;
}
I am not getting "Listening...".
It was due to the sdtout data buffered. Doing fflush(stdout), gave the proper print. And the process is now blocked at expected position accept().
I wrote a broadcaster and a listener which recvfrom a particular port on the system. I used REUSEADDR option for the socket in listener, to make multiple instances of listener monitor the same port on same system.
When I run the listener on different machines, and send packet from another machine, all the listeners receives the packet. but when I run multiple instances of listener on same machine and if I try sending udp packets, only the first instance of listener gets the packet not all. I want to broadcast UDP packets on same machine and want all the listeners to receive the packet. I am on linux.
I followed the Beej's Guide.
Edit 01
listener code
/*
** listener.c -- a datagram sockets "server" demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MYPORT "4950" // the port users will be connecting to
#define MAXBUFLEN 100
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
int reuse_addr = 1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("listener: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
if(errno == EADDRINUSE)
{
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&reuse_addr, sizeof reuse_addr) < 0)
perror("setsockopt(): REUSEADDR\n"),exit(1);
}
else
{
close(sockfd);
perror("listener: bind");
continue;
}
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
printf("listener: waiting to recvfrom...\n");
addr_len = sizeof their_addr;
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
printf("listener: got packet from %s\n",
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s));
printf("listener: packet is %d bytes long\n", numbytes);
buf[numbytes] = '\0';
printf("listener: packet contains \"%s\"\n", buf);
close(sockfd);
return 0;
}
Set socket option to SO_REUSEPORT in linstener.
SO_REUSEPORT - socket option allows multiple sockets on the same host to bind to the same port
Make accept_local as 1 using echo in SYSFS.
/proc/sys/net/ipv4/conf/all # cat accept_local
1
This will allow local communication.
I have a socket server code as below:
/*
** server.c -- a stream socket server demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!", 13, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
It uses port 3490. When I run it on my Mac OS X, I try to use the netstat command to find this connection according to the port number:
netstat -anpt | grep 3490
the output is empty. What's the reason for it?
Linux and OS X's implementations of netstat are quite a bit different from each other, so you can't just take a netstat command that works on linux and expect it to work (let alone do the same thing) on OS X. In particular, OS X's netstat doesn't have a -t option, and it has -p, but it means something completely different from linux's (actually, on OS X, -p shows stats for the specified protocol, and since "t" isn't a recognized protocol, you should get an error from this command).
I presume the actual goal here is to find out about the process listening on port 3490? If so, OS X's netstat command can't do this; it has no capability to display information about the process attached to the port. For that, you need lsof: lsof -itcp:3490. Note that lsof cannot check processes you don't own; if the socket server is not running under your UID, you need to sudo the lsof command in order to see it.
I am doing some testing with Unix domain sockets and I can communicate over them without an issue, however, when I call accept() on the server side of my test program, the returned struct sockaddr_un doesn't contain a sun_path.
I am pretty sure that Inet sockets have their address and port properly filled out after an accept() call, so am I doing something wrong in my test program or am I expecting the wrong outcome?
I am running CentOS 6.2 and gcc 4.4.6.
Sample Code:
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NAME "socket"
int main(int argc, char **argv)
{
int sock, msgsock, rval;
struct sockaddr_un server, client;
char buf[1024];
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
listen(sock, 5);
for (;;) {
socklen_t len = sizeof(client);
msgsock = accept(sock, (struct sockaddr *)&client, &len);
if (msgsock == -1)
perror("accept");
else do {
printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
return 0;
}
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define DATA "Half a league, half a league . . ."
int main(int argc, char **argv)
{
int sock;
struct sockaddr_un server;
if (argc < 2) {
printf("usage:%s <pathname>", argv[0]);
exit(1);
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, argv[1]);
if (connect(sock, (struct sockaddr *) &server,
sizeof(struct sockaddr_un)) < 0) {
close(sock);
perror("connecting stream socket");
exit(1);
}
if (write(sock, DATA, sizeof(DATA)) < 0)
perror("writing on stream socket");
close(sock);
return 0;
}
Just to reiterate the question:
Why isn't sun_path filled out after the accept() call on the server?
I am really not sure if this is an answer at all. Probably it's more like musings about some research, though maybe still worth while reading.
The value filled by accept(2) seems to be quite protocol agnostic at least in Linux 3.16.0, NetBSD 6.1.4 and Darwin 13.1.0 kernels. In practice this means that the second parameter to accept(2), struct sockaddr * gets filled only up to what is shared between all protocols. So what you have in your hands after a successful acccept(2) is far from being a complete struct sockaddr_un.
Probably nobody thought it would be of much importance at the time first implementations of accept(2) were done and now we're stuck with this. Fortunately there is a way around that, in case one has lost the pathname used for socket with call to bind(2), and would now like to find it again.
With struct sockaddr_storage and getsockname(2) the member sun_path is accessible. So, to make sure you are getting all juicy details, call getsockname(2) after a successful call to accept(2) (this would be put after line number 40 in your server.c):
struct sockaddr_storage ss;
socklen_t sslen = sizeof(struct sockaddr_storage);
if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
struct sockaddr_un *un = (struct sockaddr_un *)&ss;
printf("socket name is: %s\n", un->sun_path);
}
Or just use this:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NAME "socket"
int main(int argc, char **argv)
{
int sock, msgsock, rval;
struct sockaddr_un server, client;
char buf[1024];
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
listen(sock, 5);
for (;;) {
socklen_t len = sizeof(client);
msgsock = accept(sock, (struct sockaddr *)&client, &len);
if (msgsock == -1)
perror("accept");
else do {
printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));
struct sockaddr_storage ss;
socklen_t sslen = sizeof(struct sockaddr_storage);
if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
struct sockaddr_un *un = (struct sockaddr_un *)&ss;
printf("socket name is: %s\n", un->sun_path);
}
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
return 0;
}
This has been tested, ie. it compiles and produces expected results, to work on a GNU/Linux system running kernel 3.16.0, a NetBSD system running 6.1.4 kernel, and a system equipped with OS/X Mavericks, running 13.1.0 kernel. In all of these behaviour of accept(2) is consistent: sun_path is nowhere to be found in the structure filled. The behaviour of getsockname(2) is consistent between different operating environments too, making all protocol specific details available.
You haven't bound your client socket to an address.
You don't need to bind your client socket to an address for connect() to work; but, if you expect to access your client address in your server, then you do have to bind().
Make sense?
Just setup a call to bind() before you connect in your client. Make sure you the path you use for you client is valid and check for errors as normal.
We are trying to communicate with the server listening on Linux loopback interface via raw socket and it seems like the server does not get a single packet from us. The packets we send are visible in Wireshark.
Is raw socket on loopback possible at all? (Please, don't ask why we need it: it's too complicated to explain here)
EDIT: this is how we open it
_I_RawSocket = socket( PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)))
memset( &ifr, 0, sizeof( ifr ) );
strcpy( ifr.ifr_ifrn.ifrn_name, _InterfaceName);
ioctl( _I_RawSocket, SIOCGIFINDEX, &ifr )
memset( &sll, 0, sizeof( sll ) );
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons( ETH_P_ALL );
bind( _I_RawSocket, (struct sockaddr *) &sll, sizeof( sll ))
The server is lighttpd and it's reachable via normal socket on localhost.
netstat --raw prints empty table but I'm absolutely sure we have two functional raw sockets on normal eth devices.
Raw sockets behave particularly fizzy with bind() and connect(), but I can't confirm that your issue lies with them. I suggest you follow a more straightforward approach:
Sender
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define DEST "127.0.0.1"
int main(int argc, char **argv)
{
int s;
struct sockaddr_in dst_addr;
char packet[50];
struct iphdr *ip = (struct iphdr *)packet;
if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = 0; /* not needed in SOCK_RAW */
inet_pton(AF_INET, DEST, (struct in_addr *)&dst_addr.sin_addr.s_addr);
memset(dst_addr.sin_zero, 0, sizeof(dst_addr.sin_zero));
memset(packet, 'A', sizeof(packet)); /* payload will be all As */
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(40);
ip->frag_off = 0; /* NF */
ip->ttl = 64;
ip->protocol = IPPROTO_RAW; /* this has to be IPPROTO_RAW */
ip->check = 0;
ip->saddr = dst_addr.sin_addr.s_addr;
ip->daddr = dst_addr.sin_addr.s_addr;
while(42) {
sleep(5);
if (sendto(s, packet, sizeof(packet), 0,
(struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr)) < 0)
perror("uh oh:");
}
return(0);
}
Receiver
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int s;
struct sockaddr_in src_addr;
char packet[50];
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
memset(packet, 0, sizeof(packet));
socklen_t *len = (socklen_t *)sizeof(src_addr);
int fromlen = sizeof(src_addr);
while(42) {
if (recvfrom(s, &packet, sizeof(packet), 0,
(struct sockaddr *)&src_addr, &fromlen) < 0)
perror("uh oh:");
int i = sizeof(struct iphdr); /* print the payload */
for(; i < sizeof(packet); i++) {
printf("%c", packet[i]);
}
printf("\n");
}
return(0);
}
I hope these behave exactly like you want them to. Read man 7 raw for the gory details of why this works and more importantly man 7 packet if you want to extend it. Also, take note that IPPROTO_RAW implies the IP_HDRINCL socket option, which is why we're constructing the ip header ourselves - although the IP checksum and total length are computed and filled in by the kernel, still.
edit: In addition, if you wanted a raw socket with which to send valid data to an application like lighttpd, you'd have to match the protocol argument to socket() as well as provide valid values for the IP header fields. A proper ethernet header is not mandatory - the only important field will be filled for you by the kernel stack.
Please make sure bind to if_index
if (ioctl(sock, SIOCGIFINDEX, &stEthReq ) < 0 )
{
printf( "failed to get IF index!" );
return -1;
}
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sll_family = AF_PACKET;
client_addr.sll_ifindex = stEthReq.ifr_ifru.ifru_ivalue;
client_addr.sll_protocol = VOS_HTONS(usEthType);
ret = bind(sock,(struct sockaddr *)(&client_addr), sizeof(client_addr));