EAGAIN error for accept on blocking socket - linux

I am using a blocking socket to accept connection. I rarely get this error which makes it hard to debug. The accept returns with EAGAIN error. How can that be for a blocking socket?

If the socket has a receive timeout set (with the SO_RCVTIMEO socket option), then accept will return EAGAIN when the timeout expires.
This code will demonstrate it (and also let you investigate the effect of delivering a signal):
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#define TESTEXIT(s, f) if (s < 0) { perror(f); exit(1); }
void handler(int x)
{
return;
}
int main()
{
int s;
int r;
struct sockaddr_in sin;
socklen_t sin_len = sizeof sin;
struct timeval timeo = { .tv_sec = 5, .tv_usec = 0 };
signal(SIGUSR1, handler);
s = socket(PF_INET, SOCK_STREAM, 0);
TESTEXIT(s, "socket");
r = listen(s, 10);
TESTEXIT(r, "listen");
r = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof timeo);
TESTEXIT(r, "setsockopt");
r = accept(s, (struct sockaddr *)&sin, &sin_len);
TESTEXIT(r, "accept");
return 0;
}

Related

NETLINK_NFLOG support in Linux 4.14

I've this program which runs fine on Linux 2.6.34. While porting this program to 4.14, socket creation is giving error "Error: : Protocol not supported". As per http://man7.org/linux/man-pages/man7/netlink.7.html
NETLINK_NFLOG (up to and including Linux 3.16)
Netfilter/iptables ULOG.
Do we know what is the alternative in 4.14 ?
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <pthread.h>
#include <netinet/ip.h>
#include <linux/types.h>
#include <linux/netlink.h>
int main()
{
struct sockaddr_nl addr;
int mypid;
int status;
int sockfd = -1;
/* mypid = getpid(); */
mypid = pthread_self();
sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
if (sockfd <= 0) {
printf("netlink socket() failed - rc:%d, errno:%d\n",
sockfd, errno);
perror("Error: ");
return (-1);
}
/* set up socket address */
memset(&addr, 0, sizeof (addr));
addr.nl_pid = mypid;
addr.nl_family = AF_NETLINK;
/*
nl_groups is the multicast
group ID to which the ULOG
messages will be sent.It
is bitmap of hexadecimal
format
*/
addr.nl_groups = 1;
/* bind socket to listen on
* multicast group 1 */
status = bind(sockfd, (struct sockaddr *)&addr, sizeof (addr));
if (status < 0) {
perror("bind:");
close(sockfd);
return (-1);
}
printf("socket bind successful\n");
close(sockfd);
return (0);
}
I tried to browse kernel source but couldn't identify.
I've below config
CONFIG_NETFILTER=y
# CONFIG_NETFILTER_ADVANCED is not set
CONFIG_NETFILTER_INGRESS=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_NETFILTER_NETLINK_LOG=y
# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set
CONFIG_NETFILTER_XTABLES=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_NAT=y
# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
NETLINK_NFLOG (up to and including Linux 3.16)
see worked example in libnml

server only accept once and terminate

I'm learning Unix Network Programming in Chapter5, wait and waitpid functions. I test function wait. I run the server application firstly and then the client(repeat ./a.out several times). But the server can only accept the request one time and terminated. Errno is 4.
/* server.c
*
* gcc server.c -o server
* ./server & (run in background)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <signal.h>
void sig_chld(int signum) // SIGCHLD handler
{
int stat;
pid_t pid;
pid = wait(&stat);
}
int main(void)
{
int listenfd;
int connfd;
struct sigaction act;
struct sockaddr_in addr;
socklen_t addrlen;
pid_t pid;
act.sa_handler = sig_chld; // register SIGCHLD handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
addrlen = sizeof(addr);
bzero(&addr, addrlen); // fill server address
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.0.10", &addr.sin_addr.s_addr);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bind(listenfd, (struct sockaddr *)&addr, addrlen);
listen(listenfd, 5);
while (1) { // waiting for client request
connfd = accept(listenfd, (struct sockaddr *)&addr, &addrlen);
if (connfd < 0) {
printf("connect\n");
break;
}
pid = fork();
if (pid < 0) {
exit(-1);
} else if (pid == 0) { // child
close(listenfd);
write(connfd, "hello\n", 7);
exit(0);
}
else { // parent
close(connfd);
}
}
return 0;
}
/* client.c
*
* gcc client.c
* ./a.out (repeat several times)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <errno.h>
#define MAXLINE 4096
int main(void)
{
char buf[1024];
int cliefd;
struct sockaddr_in servaddr;
socklen_t addrlen;
addrlen = sizeof(servaddr);
bzero(&servaddr, addrlen); // fill server address
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.0.10", &servaddr.sin_addr.s_addr);
cliefd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(cliefd, (struct sockaddr *)&servaddr, addrlen) < 0) {
printf("error: %d\n", errno);
exit(-1);
}
read(cliefd, buf, MAXLINE);
fputs(buf, stdout);
return 0;
}
errno value 4 is EINTR. This indicates that a system call was interrupted. In this case, the SIGCHLD is interrupting the accept system call. Keep reading further in that chapter. Below is a quote from it which specifically points that out for this particular example code:
Since the signal was caught by the parent while the parent was blocked in a slow system call (accept), the kernel causes accept to return an error of EINTR (interrupted system call). The parent does not handle this error so it aborts.
The purpose of this example is to show that when writing network programs that catch signals, we must be cognizant of interrupted system calls, and we must handle them.
It goes on further to explain how the signal can be set up to automatically restart interrupted system calls. In summary, set the SA_RESTART flag in the act.sa_flags field:
act.sa_flags |= SA_RESTART;

Example code on using PACKET_QDISC_BYPASS option on ethernet raw socket on linux

According to http://man7.org/linux/man-pages/man7/packet.7.html, there is a new option
PACKET_QDISC_BYPASS since linux 3.14. This has the potential of sending and receiving packets a lot faster.
Saw a sample code: https://github.com/netoptimizer/network-testing/blob/master/src/raw_packet_send.c, but unfortunately it doesn't have the code on sending packets.
I added some code to send packets but it has a problem with sendto.
# ./raw_packet_send p6p1 64
Lame RAW/PF_PACKET socket TX test program
Enabled kernel qdisc bypass
error sendto : Invalid argument
Not sure why. Here is the code (original raw_packet_send.c with my silly code). Please let me know how could make it work or point me to some good simple sample code. Thanks.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
//#include <stdio.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <sys/socket.h>
//#include <sys/mman.h>
//#include <linux/filter.h>
//#include <ctype.h>
//#include <fcntl.h>
//#include <unistd.h>
//#include <bits/wordsize.h>
//#include <net/ethernet.h>
//#include <netinet/ip.h>
//#include <arpa/inet.h>
//#include <stdint.h>
//#include <string.h>
//#include <assert.h>
//#include <net/if.h>
//#include <inttypes.h>
//#include <poll.h>
//#include <unistd.h>
#ifndef PACKET_QDISC_BYPASS
#define PACKET_QDISC_BYPASS 20
#endif
#include "common_socket.h"
char pkt[2000] = {0x00, 1,2,3,4,0, 0,1,2,3,4,1, 8, 0};;
int len = 96;
char intfName[100] = "em1";
/* Avail in kernel >= 3.14
* in commit d346a3fae3 (packet: introduce PACKET_QDISC_BYPASS socket option)
*/
void set_sock_qdisc_bypass(int fd, int verbose)
{
int ret, val = 1;
ret = setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, &val, sizeof(val));
if (ret < 0) {
printf("[DEBUG] %s(): err:%d errno:%d\n", __func__, ret, errno);
if (errno == ENOPROTOOPT) {
if (verbose)
printf("No kernel support for PACKET_QDISC_BYPASS"
" (kernel < 3.14?)\n");
} else {
perror("Cannot set PACKET_QDISC_BYPASS");
}
} else
if (verbose) printf("Enabled kernel qdisc bypass\n");
}
int pf_tx_socket(int ver)
{
int ret, val = 1;
/* Don't use proto htons(ETH_P_ALL) as we only want to transmit */
int sock = socket(PF_PACKET, SOCK_RAW, 0);
//int sock = socket(AF_INET,SOCK_PACKET,htons(3));
if (sock == -1) {
perror("Creation of RAW PF_SOCKET failed!\n");
exit(1);
}
ret = Setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver));
return sock;
}
void mybind(int sock, char *intf) {
struct ifreq ifr;
int rc;
memset((char*)&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), intf);
if ((rc = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0)
{
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sock);
exit(-1);
}
}
int flood (int sock) {
struct sockaddr intfAddrs;
char cmd[100];
int tmp;
memset((char*)&intfAddrs, 0, sizeof (struct sockaddr));
intfAddrs.sa_family = PF_PACKET;
strcpy((char*)(intfAddrs.sa_data), intfName);
sprintf(cmd, "ifconfig %s promisc", intfName); system(cmd);
while (1) {
while (1) {
tmp = sendto(sock, pkt, len, 0, &intfAddrs, sizeof(intfAddrs));
if (tmp != len) {perror("error sendto "); exit(0); }
}
}
}
int main(int argc, char **argv)
{
if (argc > 1) { strcpy(intfName, argv[1]); }
if (argc > 2) { len = atoi(argv[2]); }
printf("Lame RAW/PF_PACKET socket TX test program\n");
int sock = pf_tx_socket(0);
set_sock_qdisc_bypass(sock, 1);
mybind(sock, intfName);
flood(sock);
return 0;
}
I think you need to change from PF_PACKET to AF_PACKET (although it looks like PF_PACKET is an alias to AF_PACKET) but I don't have any success when using PF_PACKET.
This code works for me:
// Create a raw socket
sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd == -1) {
perror("Can't create AF_PACKET socket");
return EXIT_FAILURE;
}
// Check kernel version
static const int32_t sock_qdisc_bypass = 1;
int32_t sock_qdisc_ret = setsockopt(thd_opt->sock_fd, SOL_PACKET, PACKET_QDISC_BYPASS, &sock_qdisc_bypass, sizeof(sock_qdisc_bypass));
if (sock_qdisc_ret == -1) {
perror("Can't enable QDISC bypass on socket");
return EXIT_FAILURE;
}
This is from setup_socket_mmap() here

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.

Can't read any data when connecting to Redis

I have the following code with connects to Redis running locally on port 6379. The problem is that can't read any output, the reader thread just blocks on recvfrom(). Everything works fine with an echo server for example, but not with Redis.
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
struct sockaddr_in servAddr;
int sock;
/* Reader loop */
void *readerf(void *n) {
char buffer[128];
int bytesRcvd;
while((bytesRcvd = recvfrom(sock, buffer, 127, 0, NULL, 0)) > 0) {
printf("[%s]",buffer);
}
}
char *getk = "GET key\n";
char *setk = "SET key \"test\"\n";
void sendd(char *str) {
if (send(sock, str, strlen(str), 0) != strlen(str))
perror("Send error");
send(sock, "\0", 1, 0);
//printf("sent: [%s]",str);
}
int main(int argc, char *argv[]) {
int i = 1;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return 1;
/* Construct the server address structure */
memset(&servAddr, 0, sizeof(servAddr)); /* Zero out structure */
servAddr.sin_family = AF_INET; /* Internet address family */
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /* Server IP address */
servAddr.sin_port = htons(6379); /* Server port */
if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr))<0)
perror("Could not connect");
sendd(getk);
sendd(setk);
sendd(getk);
pthread_t reader;
pthread_create(&reader, NULL, readerf, &i);
sleep(5);
close(sock);
return 0;
}
It seems like you are not properly implementing Redis' protocol: http://redis.io/topics/protocol
If you are seriously coding a client, and not just investigating/having fun consider hiredis: https://github.com/antirez/hiredis/

Resources