Apache actively close tcp connections when keep-alive is set by the client - apache

I'm trying to do Apache performance benchmarking for my course project. But I meet a strange problem. When I use a single client to establish multiple TCP connections (e.g. 100) to an Apache server and send HTTP 1.1 requests with the Connection: keep-alive header, I suppose the TCP connections can be reused. But the Apache server will actively terminate TCP connections, even if Connection: Keep-Alive and Keep-Alive: xxx are included in the HTTP response header.
this is my client code (I obfuscate the IP address):
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
#define DEBUG
#define MAX_SOCKETS_NUM 100
#define BUF_LEN 4096
int main() {
int i;
struct sockaddr_in server_addr, peer_addr;
int res, len = sizeof(peer_addr);
char *req = "GET /20KB HTTP/1.1\r\n"
"Host: x.x.x.192\r\n"
"Connection: keep-alive\r\n"
"Upgrade-Insecure-Requests: 1\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: zh-CN,zh;q=0.9\r\n"
"\r\n";
char buf[BUF_LEN + 1];
int sockets[MAX_SOCKETS_NUM];
int estb_num = 0;
int estb_map[MAX_SOCKETS_NUM];
struct timeval goal, now, interval;
// create sockets
for (i = 0; i < MAX_SOCKETS_NUM; i++) {
if ((sockets[i] = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) {
printf("Socket %d error!\n", i);
return -1;
}
}
// initialize server_addr
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
if (inet_pton(AF_INET, "x.x.x.192", &server_addr.sin_addr) <= 0) {
printf("Invalid address/Address not supported\n");
return -1;
}
// connect to the victim server
for (i = 0; i < MAX_SOCKETS_NUM; i++) {
if ((res = connect(sockets[i], (struct sockaddr *)&server_addr, sizeof(server_addr))) == 0) {
#ifdef DEBUG
printf("Socket %d connected immediately.\n", i);
#endif
estb_map[i] = 1;
estb_num++;
}
else if (res == -1) {
if (errno != EINPROGRESS) {
printf("Error occured when connect() is called on socket %d.\n", i);
return -1;
}
#ifdef DEBUG
printf("Socket %d sends SYN packet but the ACK is not received.\n", i);
#endif
}
}
while (1) {
if (estb_num == MAX_SOCKETS_NUM)
break;
for (i = 0; i < MAX_SOCKETS_NUM; i++) {
while (1) {
res = getpeername(sockets[i], (struct sockaddr *)&peer_addr, &len);
if (res == 0) {
estb_num++;
#ifdef DEBUG
printf("Socket %d connects successfully.\n", i);
#endif
break;
}
}
}
}
interval = (struct timeval) {
.tv_sec = 1,
.tv_usec = 0
};
len = strlen(req);
while (1) {
gettimeofday(&now, NULL);
timeradd(&now, &interval, &goal);
#ifdef DEBUG
printf("%ld.%ld\n", goal.tv_sec, goal.tv_usec);
#endif
// send requests
for (i = 0; i < MAX_SOCKETS_NUM; i++) {
res = send(sockets[i], req, len, 0);
if (res == -1) {
printf("socket:%ld errno:%ld\n", i, errno);
}
}
while (1) {
for (i = 0; i < MAX_SOCKETS_NUM; i++) {
res = recv(sockets[i], buf, BUF_LEN, 0);
if (res == 0) {
struct sockaddr_in dbg_addr;
int dgb_addr_len = sizeof(struct sockaddr_in);
getsockname(sockets[i], &dbg_addr, &dgb_addr_len);
printf("socket:%d port:%d\n", i, ntohs(dbg_addr.sin_port));
goto end;
}
else if (res == -1) {
if (errno != EAGAIN)
printf("Error occurs when recv is called.\n");
}
else {
// do nothing because we don't need the response
}
}
gettimeofday(&now, NULL);
if (timercmp(&now, &goal, >) != 0) {
#ifdef DEBUG
printf("%d.%d\n", now.tv_sec, now.tv_usec);
#endif
break;
}
}
}
end:
return 0;
}
the workflow of the client (with the IP address x.x.x.198) is:
establish 100 tcp connections to the server (with the IP address x.x.x.192) using non-blocking socket.
send the same requests on the 100 tcp connections
invoking non-blocking recv repeatedly on these 100 sockets for 1 second
goto 2.
one execution of the program generates the following output:
Socket 0 sends SYN packet but the ACK is not received.
(some lines are omitted)
Socket 99 sends SYN packet but the ACK is not received.
Socket 0 connects successfully.
(some lines are omitted)
Socket 99 connects successfully.
1639450192.343129
1639450192.343155
1639450193.343163
socket:63 port:56804
The output indicates in the second round of requests (the first round is finished because it prints two timestamps, but the second round only prints one), the recv function on the 63th socket with the local port number 56804 returns 0, which means the Apache server actively terminates tcp connections. And I dumped all the packets on the client using tcpdump, the following figure shows the packet trace of the connection with the local port number 56804:
the packet trace shows the same result that the server actively sends tcp FIN packet to the client to terminate the TCP connection. But we can see Connection: Keep-Alive and Keep-Alive: timeout=10m, max=1999 are included in the response header which means the Apache server handles keep-alive correctly.
The server runs Ubuntu 20.04.3 and Apache 2.4.41.
I's very confused about why this happens, why will Apache close keep-alive connections? I'd be appreciate if you can help me, thanks!

From this document:
A host MAY keep an idle connection open for longer than the time that
it indicates, but it SHOULD attempt to retain a connection for at
least as long as indicated.
Capital letters there are key, and written like this in the document:
Your case matches the "SHOULD" part. E.g. keep-alive is a recommendation - but if the server needs those resources (or is configured to have less open connections than the number of your clients) it's free to close them at will. Your clients will need to deal with this state.
If the described behavior is dependent on the number of parallel sockets that you open (apart from the timeout), you're most likely running into resource limits on your server - either explicitly configured, or implicit, from default values.
Imagine how easy a DDOS attack would be if all that's required was a couple of keep-alive requests to saturate the number of concurrent connections that the server offers.
Also note that timeout is based on different perceptions of time on server (starting with sending the last packet) and client (starting when receiving the last packet)

Related

Sending message from udp client to udp server without sending protocol address of client

I have a query is it possible to send data from udp client to udp server without sending the protocol address and then from server to client
Server.c
if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
printf("Couldn't bind to the port\n");
return -1;
}
printf("Done with binding\n");
printf("Listening for incoming messages...\n\n");
// Receive client's message:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)NULL, NULL) < 0){
printf("Couldn't receive\n");
return -1;
}
// printf("Received message from IP: %s and port: %i\n",
// inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("Msg from client: %s\n", client_message);
// Respond to client:
int n = atoi(client_message);
int m =0, sum = 0;
while(n>0){
m=n%10;
sum=sum+m;
n=n/10;
}
sprintf(server_message, "%d", sum);
if (sendto(socket_desc, server_message, strlen(server_message), 0,
(struct sockaddr*)&client_addr, client_struct_length) < 0){
printf("Can't send\n");
return -1;
}
When I send data from client to server the server printed what I had sent from client but then then when I tried to send data back to client it is unable to send the modified data.
client_addr does not magically get filled with the clients address. It has to be explicitly done in recvfrom - but you use instead NULL there and thus throw away this information:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)NULL, NULL) < 0){

Sending and receiving UDP using the same port does not work with the asio library?

I'm trying to send and receive UDP packets through the same endpoint. As far as I know this should be possible. But I can not get it to work with the asio library (version 1.20.0).
This is what I do:
asio::io_context io_context;
asio::ip::udp::socket* udpSendRecvSocket = new asio::ip::udp::socket(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), 7782));
asio::error_code ec;
char data[1000];
//
// send packet
//
std::string ipAddress = "127.0.0.1";
asio::ip::address ip_address = asio::ip::address::from_string(ipAddress);
asio::ip::udp::endpoint remoteTarget_endpoint(ip_address, 5500);
udpSendRecvSocket->send_to(asio::buffer(data, 50), remoteTarget_endpoint, 0, ec);
if (ec) {
return 0;
}
//
// receive packets
//
size_t avLen = udpSendRecvSocket->available(ec);
while (avLen) {
asio::ip::udp::endpoint remote_endpoint;
size_t length = udpSendRecvSocket->receive_from(asio::buffer(data, 1000), remote_endpoint, 0, ec);
int p = remote_endpoint.port();
if (ec) {
return 0;
}
avLen -= length;
}
The receive does not work correctly. I do receive a packet that I send (from some other app). I know because avLen gets the right value. But when executing the receive_from(), if fails. And the port number in p gets the value 5500. This is the value of the target port of the send_to() call that was executed before.
The strange thing is that when I remove the send_to() call, the receive does work correctly and the p will reflect the correct port number of the sending application.
Is this a bug?

How to lower CPU usage of finish_task_switch(), called by epoll_wait?

I've written a simple epoll-driven server to bench network/io performance. The server simply receive a request and send a response immediately. It is slower than redis-server 'get', 38k/s vs 40k/s. Both use redis-benchmark as the load runner, and both used cpu up (>99%).
bench redis-server: redis-benchmark -n 1000000 -c 20 -t get -p 6379
bench myserver : redis-benchmark -n 1000000 -c 20 -t get -p 6399
I've profiled them using linux perf, eliminated epoll_ctl in myserver (as what redis-server does). Now the problems is function finish_task_switch() takes too much cpu time, about 10%-15% (for redis-server and redis-benchmark are 3%, on the same machine).
The call flow (read it top-down) is -> epoll_wait(25%) -> entry_SYSCALL_64_after_hwframe(23.56%) -> do_syscall_64(23.23%) -> sys_epoll_wait(22.36%) -> ep_poll(21.88%) -> schedule_hrtimeout_range(12.98%) -> schedule_hrtimeout_range_clock(12.74%) -> schedule(11.30%) -> _schedule(11.30%) -> finish_task_switch(10.82%)
I've tried writing the server using raw epoll api, and using redis's api in redis/src/ae.c, nothing changed.
I've examined how redis-server and redis-benchmark use epoll, no tricks found.
The redis CFLAGS is used for myserver, same as redis-benchmark.
The CPU usage has nothing to do with level/edge-triggered, block or nonblock client fd, whether epoll_wait's timeout set or not.
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> // exit
#include <string.h> // memset
#include "anet.h"
#define MAX_EVENTS 32
typedef struct {
int fd;
char querybuf[256];
} client;
client *clients;
char err[256];
#define RESPONSE_REDIS "$128\r\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n"
static int do_use_fd(client *c)
{
int n = read(c->fd, c->querybuf, sizeof(c->querybuf));
if (n == 0) { printf("Client Closed\n"); return n; }
n = write(c->fd, RESPONSE_REDIS, sizeof(RESPONSE_REDIS)-1);
return n;
}
int main()
{
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
epollfd = epoll_create(MAX_EVENTS);
listen_sock = anetTcpServer(err, 6399, NULL, MAX_EVENTS);
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev);
clients = (client *)malloc(sizeof(client) * MAX_EVENTS);
memset(clients, 0, sizeof(client) * MAX_EVENTS);
for (;;) {
int n;
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100);
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &addr, &addrlen);
anetNonBlock(err, conn_sock);
ev.events = EPOLLIN;
//ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev);
clients[conn_sock].fd = conn_sock;
} else {
client *c = &clients[events[n].data.fd];
int ret = do_use_fd(c);
if (ret == 0) {
epoll_ctl(epollfd, EPOLL_CTL_DEL, c->fd, &ev);
}
}
}
}
}
Server's listen fd is blocked. set it nonblocked lowers the usage of finish_task_switch to <2%.

Receiving data from a server using ESP8266 /Arduino Uno

I have a Raspberry Pi working as a WiFi hotspot and an Arduino Uno trying to get data from it using an ESP8266 module.
This is my receiver code for Arduino:
#include <SoftwareSerial.h>
#include <SerialESP8266wifi.h>
#define sw_serial_rx_pin 4 // Connect this pin to TX on the esp8266
#define sw_serial_tx_pin 6 // Connect this pin to RX on the esp8266
#define esp8266_reset_pin 5 // Connect this pin to CH_PD on the esp8266, not reset. (let reset be unconnected)
SoftwareSerial swSerial(sw_serial_rx_pin, sw_serial_tx_pin);
// the last parameter sets the local echo option for the ESP8266 module..
SerialESP8266wifi wifi(swSerial, swSerial, esp8266_reset_pin, Serial);//adding Serial enabled local echo and wifi debug
String inputString;
boolean stringComplete = false;
unsigned long nextPing = 0;
void setup() {
inputString.reserve(20);
swSerial.begin(9600);
Serial.begin(9600);
while (!Serial)
;
Serial.println("Starting wifi");
wifi.setTransportToTCP();// this is also default
// wifi.setTransportToUDP();//Will use UDP when connecting to server, default is TCP
wifi.endSendWithNewline(true); // Will end all transmissions with a newline and carriage return ie println.. default is true
wifi.begin();
wifi.connectToAP("RPi", "raspberry");
wifi.connectToServer("192.168.50.1", "1234");
wifi.send(SERVER, "ESP8266 test app started");
}
void loop() {
//Make sure the esp8266 is started..
if (!wifi.isStarted())
wifi.begin();
//Send what you typed in the arduino console to the server
static char buf[20];
if (stringComplete) {
inputString.toCharArray(buf, sizeof buf);
wifi.send(SERVER, buf);
inputString = "";
stringComplete = false;
}
//Send a ping once in a while..
if (millis() > nextPing) {
wifi.send(SERVER, "Ping ping..");
nextPing = millis() + 10000;
}
//Listen for incoming messages and echo back, will wait until a message is received, or max 6000ms..
WifiMessage in = wifi.listenForIncomingMessage(6000);
if (in.hasData) {
if (in.channel == SERVER)
Serial.println("Message from the server:");
else
Serial.println("Message a local client:");
Serial.println(in.message);
//Echo back;
wifi.send(in.channel, "Echo:", false);
wifi.send(in.channel, in.message);
nextPing = millis() + 10000;
}
//If you want do disconnect from the server use:
// wifi.disconnectFromServer();
}
//Listen for serial input from the console
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
}
}
When I execute, the serial monitor shows:
OK ARFa C⸮C⸮j⸮H⸮AT+AWJAP="RPi",#raspberry" WIFI DISCONNECT WIFI
CONNECVED WIFI GOT IP
OK AT+CIFSR
+CIFSR:STAIP,"192.168.50.13"
+CIFQR:STAMAC,"2c:3a:eAT+CIPSTART=4,"TCP","192.0n8.50.1",121l
Link type ERROR
Raspberry Pi's ISC DHCP server:
wlan0: STA 2c:3a:e8:4e:bf:70 RADIUS: starting accounting session
5A3B2C85-000000E9 wlan0: STA 2c:3a:e8:4e:bf:70 WPA: pairwise key
handshake completed (RSN)
I also referred to this SO thread with no luck.
Some assumptions because you have not given the info:
Arduino IDE >=1.85
ESP8266 Community package >=2.41
ESP Module ESP8266-12E with latest AT firmware
if that is the case and these fragments (enclosed by X X) are no typos
+CIFQR:STAMAC,"2c:3a:eXAT+CIPSTART=4,"TCP","192.0XnX8.50.1",121l
this leaves the following points to check
hardware connectors - serial connectors between arduino and esp module
stable power source 3.3V for the ESP module
Sure this is ok - but just in case and as reference for other readers
serial speed - try to increase from 9600 to 57600 or even to 115200 baud
These pieces of code should be in the setup() and not in the loop()
//Make sure the esp8266 is started..
if (!wifi.isStarted())
wifi.begin();
//Send what you typed in the arduino console to the server
static char buf[20];
Code processing
nextPing = millis() + 10000;
at the end of
if (in.hasData) {
might lead to interuptions in the communication
Reason due to the processing of code this might trigger at an unwanted point

winsock - Sender UDP socket not bound to the desired port

Below you can see my code that implements a pretty basic UDP sender in C++ with Winsock. The thing is that no matter how many times I run the code, the socket (the listenSocket) gets bound to a different UDP port. Is there any specific reason for this? Am I doing some mistake in my code?
thanks
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
using namespace std;
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKADDR_IN myAddress;
SOCKADDR_IN targetAddress;
int myPort = 60888;
const char *myIP = "192.168.0.1";
int remotePort = 2048;
const char *remoteIP = "192.168.0.2";
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET SendSocket = INVALID_SOCKET;
SOCKET acceptSocket;
char cBuffer[1024] = "Test Buffer";
int nBytesSent = 0;
int nBufSize = strlen(cBuffer);
int iResult;
// Initialize Winsock
if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != NO_ERROR )
{
cerr<<"Socket Initialization: Error with WSAStartup\n";
system("pause");
WSACleanup();
exit(10);
}
ListenSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ListenSocket == INVALID_SOCKET or SendSocket == INVALID_SOCKET)
{
cerr<<"Socket Initialization: Error creating socket"<<endl;
system("pause");
WSACleanup();
exit(11);
}
//bind
myAddress.sin_family = AF_INET;
myAddress.sin_addr.s_addr = inet_addr(myIP);
myAddress.sin_port = htons(myPort);
targetAddress.sin_family = AF_INET;
targetAddress.sin_addr.s_addr = inet_addr(remoteIP);
targetAddress.sin_port = htons(remotePort);
if(bind(ListenSocket, (SOCKADDR*) &myAddress, sizeof(myAddress)) == SOCKET_ERROR)
{
cerr<<"ServerSocket: Failed to connect\n";
system("pause");
WSACleanup();
exit(14);
}
else
printf("Server: bind() is OK.\n");
nBytesSent = sendto(SendSocket, cBuffer, nBufSize, 0,
(SOCKADDR *) &targetAddress,
sizeof(SOCKADDR_IN));
printf("Everything is ok\n");
system("PAUSE");
closesocket(ListenSocket);
closesocket(SendSocket);
return EXIT_SUCCESS;
}
EDIT: Maybe I was not so clear. What I do with this code is to send some data to a remote PC. But what is required is that the UDP segments should appear to be originated from a specific port. How can this be done? Is it wrong what I'm doing here? Now that I'm thinking of it, I guess it is wrong indeed. The SendSocket and ListenSocket don't have any connection, correct? So, how can I make it that the UDP segments appear to originate from a specific UDP port? Thanks!
You are not calling bind() on SendSocket before sending data with it, so WinSock is free to bind that socket to whatever random local IP/Port it needs to. If you have to send data with a specific source IP/Port every time, you have to bind() to that IP/Port first. If that local IP/Port is the same pair you are binding ListenSocket to, then you don't need to use two separate sockets to begin with. You can send data with the same socket that is listening for incoming data.