Skip to content

Instantly share code, notes, and snippets.

@nishidy
Created April 2, 2019 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nishidy/46f034239428d11f43ca6d5ea4205a3b to your computer and use it in GitHub Desktop.
Save nishidy/46f034239428d11f43ca6d5ea4205a3b to your computer and use it in GitHub Desktop.
TCP_DEFER_ACCEPT test program for server
#include <stdio.h> //printf(), fprintf(), perror()
#include <sys/socket.h> //socket(), connect(), recv()
#include <arpa/inet.h> // struct sockaddr_in, struct sockaddr, inet_ntoa(), inet_aton()
#include <stdlib.h> //atoi(), exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <string.h> //memset()
#include <unistd.h> //close()
#include <linux/tcp.h> //TCP_DEFER_ACCEPT
#include <time.h> //
#define MSGSIZE 32
#define MAX_MSGSIZE 1024
#define BUFSIZE (MSGSIZE + 1)
#define QUEUELIMIT 5
int main(int argc, char *argv[]){
int servSock;
struct sockaddr_in servSockAddr;
char recvBuffer[BUFSIZE];
char sendBuffer[BUFSIZE];
int byteRcvd;
int clientSock;
struct sockaddr_in clientSockAddr;
unsigned int clientLen;
unsigned int recvLen, sendLen;
int defer = 1;
time_t curtime;
FILE *fp;
char buf[BUFSIZE];
if((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
perror("socket() failed");
exit(EXIT_FAILURE);
}
memset(&servSockAddr, 0, sizeof(servSockAddr));
servSockAddr.sin_family = AF_INET;
servSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servSockAddr.sin_port = htons(8080);
if(bind(servSock, (struct sockaddr *) &servSockAddr, sizeof(servSockAddr)) < 0){
perror("bind() failed.");
exit(EXIT_FAILURE);
}
if(listen(servSock, QUEUELIMIT)<0){
perror("listen() failed.");
exit(EXIT_FAILURE);
}
setsockopt(servSock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &defer, sizeof(defer) );
while(1){
if((clientSock = accept(servSock, (struct sockaddr *) &clientSockAddr, &clientLen))<0){
perror("accept() failed.");
exit(EXIT_FAILURE);
}
curtime = time(NULL);
//printf("[%s] accept() unblocked.\n",ctime(&curtime));
if( (fp = popen("date +%T.%3N","r")) == NULL){
perror("popen() failed.");
exit(EXIT_FAILURE);
}
fgets(buf, BUFSIZE, fp);
buf[strlen(buf)-1]='\0';
printf("[%s] accept() unblocked.\n",buf);
while(1){
if((recvLen = recv(clientSock, recvBuffer, BUFSIZE, 0)) < 0){
printf("recv() failed.");
exit(EXIT_FAILURE);
} else if (recvLen == 0){
printf("connection closed by foreign host.\n");
break;
}
if( (fp = popen("date +%T.%3N","r")) == NULL){
perror("popen() failed.");
exit(EXIT_FAILURE);
}
fgets(buf, BUFSIZE, fp);
buf[strlen(buf)-1]='\0';
printf("[%s] received: %s\n",buf,recvBuffer);
sprintf(sendBuffer, "HTTP/1.1 200 OK");
if((sendLen = send(clientSock, sendBuffer, strlen(sendBuffer), 0)) < 0){
perror("send() failed.");
exit(EXIT_FAILURE);
} else if (sendLen == 0){
printf("connection closed by foreign host.\n");
break;
}
}
close(clientSock);
}
return 0;
}
@nishidy
Copy link
Author

nishidy commented Apr 2, 2019

When TCP_DEFER_ACCEPT was not enabled, accept() was unblocked before the data was received.
When it was enabled, accept() was unblocked right after the data was received.

$ ./server              
[15:15:06.320] accept() unblocked.
[15:15:07.321] received: GET / HTTP/1.1
Host: localhost
connection closed by foreign host.
^C
$ vim server.c
$ gcc server.c -o server
$ ./server              
[15:15:26.432] accept() unblocked.
[15:15:26.433] received: GET / HTTP/1.1
Host: localhost
connection closed by foreign host.
$ sudo tcpdump -i lo tcp port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
15:15:06.319196 IP localhost.42082 > localhost.webcache: Flags [S], seq 3772620183, win 43690, options [mss 65495,sackOK,TS val 3739864161 ecr 0,nop,wscale 7], length 0
15:15:06.319206 IP localhost.webcache > localhost.42082: Flags [S.], seq 562650840, ack 3772620184, win 43690, options [mss 65495,sackOK,TS val 3739864161 ecr 3739864161,nop,wscale 7], length 0
15:15:06.319217 IP localhost.42082 > localhost.webcache: Flags [.], ack 1, win 342, options [nop,nop,TS val 3739864161 ecr 3739864161], length 0
15:15:07.319678 IP localhost.42082 > localhost.webcache: Flags [P.], seq 1:33, ack 1, win 342, options [nop,nop,TS val 3739865161 ecr 3739864161], length 32: HTTP: GET / HTTP/1.1
15:15:07.319691 IP localhost.webcache > localhost.42082: Flags [.], ack 33, win 342, options [nop,nop,TS val 3739865161 ecr 3739865161], length 0
15:15:07.321683 IP localhost.webcache > localhost.42082: Flags [P.], seq 1:16, ack 33, win 342, options [nop,nop,TS val 3739865163 ecr 3739865161], length 15: HTTP: HTTP/1.1 200 OK[!http]
15:15:07.321690 IP localhost.42082 > localhost.webcache: Flags [.], ack 16, win 342, options [nop,nop,TS val 3739865163 ecr 3739865163], length 0
15:15:07.321832 IP localhost.42082 > localhost.webcache: Flags [F.], seq 33, ack 16, win 342, options [nop,nop,TS val 3739865164 ecr 3739865163], length 0
15:15:07.321941 IP localhost.webcache > localhost.42082: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 3739865164 ecr 3739865164], length 0
15:15:07.321954 IP localhost.42082 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 3739865164 ecr 3739865164], length 0

15:15:25.430071 IP localhost.42084 > localhost.webcache: Flags [S], seq 3822494543, win 43690, options [mss 65495,sackOK,TS val 3739883272 ecr 0,nop,wscale 7], length 0
15:15:25.430081 IP localhost.webcache > localhost.42084: Flags [S.], seq 1806699571, ack 3822494544, win 43690, options [mss 65495,sackOK,TS val 3739883272 ecr 3739883272,nop,wscale 7], length 0
15:15:25.430091 IP localhost.42084 > localhost.webcache: Flags [.], ack 1, win 342, options [nop,nop,TS val 3739883272 ecr 3739883272], length 0
15:15:26.430405 IP localhost.42084 > localhost.webcache: Flags [P.], seq 1:33, ack 1, win 342, options [nop,nop,TS val 3739884272 ecr 3739883272], length 32: HTTP: GET / HTTP/1.1
15:15:26.430431 IP localhost.webcache > localhost.42084: Flags [.], ack 33, win 342, options [nop,nop,TS val 3739884272 ecr 3739884272], length 0
15:15:26.434057 IP localhost.webcache > localhost.42084: Flags [P.], seq 1:16, ack 33, win 342, options [nop,nop,TS val 3739884276 ecr 3739884272], length 15: HTTP: HTTP/1.1 200 OK[!http]
15:15:26.434066 IP localhost.42084 > localhost.webcache: Flags [.], ack 16, win 342, options [nop,nop,TS val 3739884276 ecr 3739884276], length 0
15:15:26.434216 IP localhost.42084 > localhost.webcache: Flags [F.], seq 33, ack 16, win 342, options [nop,nop,TS val 3739884276 ecr 3739884276], length 0
15:15:26.434505 IP localhost.webcache > localhost.42084: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 3739884276 ecr 3739884276], length 0
15:15:26.434517 IP localhost.42084 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 3739884276 ecr 3739884276], length 0

@nishidy
Copy link
Author

nishidy commented Apr 2, 2019

I want to confirm if TCP_DEFER_ACCEPT recovers from a packet loss of final ACK.
However, even if TCP_DEFER_ACCEPT is not enabled, it recovers from the packet loss.
I drop the packet by iptables as follows.
You can see it works because FIN packets are re-sent continuously since ACK packet is discarded.
On the other hand, data packets are sent successfully.

$ grep setsockopt server.c
	//setsockopt(servSock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &defer, sizeof(defer) );
$ gcc server.c -o server 
$ ./server      
[16:34:35.045] accept() unblocked.
[16:34:35.046] received: GET / HTTP/1.1
Host: localhost
connection closed by foreign host.
$ sudo iptables -A INPUT -s 127.0.0.1 -p tcp --tcp-flags SYN,RST,ACK,FIN,PSH ACK -j REJECT
$ sudo tcpdump -i lo tcp port 8080
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes

16:34:34.043650 IP localhost.47130 > localhost.webcache: Flags [S], seq 808598401, win 43690, options [mss 65495,sackOK,TS val 406069374 ecr 0,nop,wscale 7], length 0
16:34:34.043661 IP localhost.webcache > localhost.47130: Flags [S.], seq 3393701262, ack 808598402, win 43690, options [mss 65495,sackOK,TS val 406069375 ecr 406069374,nop,wscale 7], length 0
16:34:34.043671 IP localhost.47130 > localhost.webcache: Flags [.], ack 1, win 342, options [nop,nop,TS val 406069375 ecr 406069375], length 0
16:34:35.043931 IP localhost.47130 > localhost.webcache: Flags [P.], seq 1:33, ack 1, win 342, options [nop,nop,TS val 406070375 ecr 406069375], length 32: HTTP: GET / HTTP/1.1
16:34:35.043956 IP localhost.webcache > localhost.47130: Flags [.], ack 33, win 342, options [nop,nop,TS val 406070375 ecr 406070375], length 0
16:34:35.046965 IP localhost.webcache > localhost.47130: Flags [P.], seq 1:16, ack 33, win 342, options [nop,nop,TS val 406070378 ecr 406070375], length 15: HTTP: HTTP/1.1 200 OK[!http]
16:34:35.046974 IP localhost.47130 > localhost.webcache: Flags [.], ack 16, win 342, options [nop,nop,TS val 406070378 ecr 406070378], length 0
16:34:35.047200 IP localhost.47130 > localhost.webcache: Flags [F.], seq 33, ack 16, win 342, options [nop,nop,TS val 406070378 ecr 406070378], length 0
16:34:35.047230 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406070378 ecr 406070378], length 0
16:34:35.047240 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406070378 ecr 406070378], length 0
16:34:37.019592 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406072350 ecr 406070378], length 0
16:34:37.019607 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406072350 ecr 406070378], length 0
16:34:40.027580 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406075358 ecr 406070378], length 0
16:34:40.027595 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406075358 ecr 406070378], length 0
16:34:45.915577 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406081246 ecr 406070378], length 0
16:34:45.915591 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406081246 ecr 406070378], length 0
16:34:57.691576 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406093022 ecr 406070378], length 0
16:34:57.691588 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406093022 ecr 406070378], length 0
16:35:22.011602 IP localhost.webcache > localhost.47130: Flags [F.], seq 16, ack 34, win 342, options [nop,nop,TS val 406117342 ecr 406070378], length 0
16:35:22.011613 IP localhost.47130 > localhost.webcache: Flags [.], ack 17, win 342, options [nop,nop,TS val 406117343 ecr 406070378], length 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment