Linux TCP socket timestamping option - sockets

Quoting form this online kernel doc
SO_TIMESTAMPING
Generates timestamps on reception, transmission or both. Supports
multiple timestamp sources, including hardware. Supports generating
timestamps for stream sockets.
Linux supports TCP timestamping, and I tried to write some demo code to get any timestamp for TCP packet.
The server code as below:
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts("Waiting for incoming connections...");
int c = sizeof(struct sockaddr_in);
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
// Note: I am trying to get software timestamp only here..
int oval = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
int olen = sizeof( oval );
if ( setsockopt( client_sock, SOL_SOCKET, SO_TIMESTAMPING, &oval, olen ) < 0 )
{ perror( "setsockopt TIMESTAMP"); exit(1); }
puts("Connection accepted");
char buf[] = "----------------------------------------";
int len = strlen( buf );
struct iovec myiov[1] = { {buf, len } };
unsigned char cbuf[ 40 ] = { 0 };
int clen = sizeof( cbuf );
struct msghdr mymsghdr = { 0 };
mymsghdr.msg_name = NULL;
mymsghdr.msg_namelen = 0;
mymsghdr.msg_iov = myiov;
mymsghdr.msg_iovlen = 1;
mymsghdr.msg_control = cbuf;
mymsghdr.msg_controllen = clen;
mymsghdr.msg_flags = 0;
int read_size = recvmsg( client_sock, &mymsghdr, 0);
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
else
{
struct msghdr *msgp = &mymsghdr;
printf("msg received: %s \n",(char*)msgp->msg_iov[0].iov_base);// This line is successfully hit.
// Additional info: print msgp->msg_controllen inside gdb is 0.
struct cmsghdr *cmsg;
for ( cmsg = CMSG_FIRSTHDR( msgp );
cmsg != NULL;
cmsg = CMSG_NXTHDR( msgp, cmsg ) )
{
printf("Time GOT!\n"); // <-- This line is not hit.
if (( cmsg->cmsg_level == SOL_SOCKET )
&&( cmsg->cmsg_type == SO_TIMESTAMPING ))
printf("TIME GOT2\n");// <-- of course , this line is not hit
}
}
Any ideas why no timestamping is available here ? Thanks
Solution
I am able to get the software timestamp along with hardware timestamp using onload with solarflare NIC.
Still no idea how to get software timestamp alone.

The link you gave, in the comments at the end, says:
I've discovered why it doesn't work. SIOCGSTAMP only works for UDP
packets or RAW sockets, but does not work for TCP. – Gio Mar 17 '16 at 9:331
it doesn't make sense to ask for timestamps for TCP, because there's
no direct correlation between arriving packets and data becoming
available. If you really want timestamps for TCP you'll have to use
RAW sockets and implement your own TCP stack (or use a userspace TCP
library). – ecatmur Jul 4 '16 at 10:39

Related

socket connection lost using select function

I'm newbee in socket program.
I made my server program with good sample program using select function.
It works well about 20,000 connections over.
But, in some case, connection accept twice consequence without
receive data from first socket.
Only data received from second socket connection.
After that, first socket resource cannot release.
FD_SET and FD_ISSET are not working with first socket in case of consequence accept I think.
Working clients are 6.
Before this situation,
accept, receive data, and close socket, accept, rcv
data, close, ...
In case, accept,
accept, receive data from second socket, and close second socket.
Lost first socket connection.
After that, accept function assign second socket descriptor.
What is problem?
How can release fisrt socket?
BR
Paul
My code is as follow:
while(1)
{
//clear the socket set
FD_ZERO (&readfds);
//add master socket to set
FD_SET (sever_socket, &readfds);
max_sd = sever_socket;
//add child sockets to set
for ( i = 0 ; i < MAX_CLIENT ; i ++)
{
//socket descriptor
sd = client_socket [i];
//if valid socket descriptor then add to read list
if (sd > 0)
{
FD_SET( sd , &readfds);
}
//highest file descriptor number, need it for the select function
if(sd > max_sd)
{
max_sd = sd;
}
}
//wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
activity = select ( max_sd + 1 , &readfds , NULL , NULL , NULL);
if ((activity < 0) && (errno!=EINTR))
{
LOG_F (WARNING, "select error");
}
//If something happened on the master socket, then its an incoming connection
if (FD_ISSET(sever_socket, &readfds))
{
if ((new_socket = accept (sever_socket, (struct sockaddr *) &address, (socklen_t*) &addrlen)) < 0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
LOG_F (INFO, "New connection, socket fd is %d, ip is : %s, port : %d",
new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
//add new socket to array of sockets
for (i = 0; i < MAX_CLIENT; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
LOG_F (INFO, "Adding to list of sockets as %d" , i);
break;
}
}
}
for (i = 0; i < MAX_CLIENT; i++)
{
sd = client_socket[i];
if (FD_ISSET (sd , &readfds))
{
memset (&rcvBuf, 0x00, sizeof(rcvBuf));
if ((fp = fdopen (sd, "r")) == NULL)
{
LOG_F (WARNING, "TCP_SOCKET FD_OPEN Error");
close (sd);
client_socket[i] = 0;
}
else
{
ret = ioctl (sd, FIONREAD, &nread);
if (nread == 0)
{
fclose (fp);
close (sd);
client_socket[i] = 0;
LOG_F (WARNING, "Client disconnected(as %d, fd %d)", i, sd);
}
else
{
len = recv (sd, rcvBuf, nread, 0);
if (len > 0)
{
LOG_F (INFO, "RECV size %d" , len);
...
do_msg_handler ()
}
}
...

How to use select() function for both TCP & UDP connection?

I have a functional server code with only TCP connection. Now I want the server to receive from UDP connection. I have used port 2000 for TCP & port 2001 for UDP. Here is a snippet of my code
struct timeval timeout; // timeout for select(), 1ms
timeout.tv_sec = 0;
timeout.tv_usec = 1000;
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// TCP port setup
int sockfd; // listening socket descriptor
int newsockfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf_tcp[256]; // buffer for client data
char buf_copy_tcp[256];
int recv_bytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR
int i, k, rv_getaddrinfo, rv_setsockopt, rv_bind, rv_listen, rv_select;
struct addrinfo hints, *servinfo, *ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rv_getaddrinfo = getaddrinfo(NULL, "2000", &hints, &servinfo);
for(ptr=servinfo; ptr!=NULL; ptr=ptr->ai_next)
{
sockfd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
rv_setsockopt = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
rv_bind = bind(sockfd, ptr->ai_addr, ptr->ai_addrlen);
break;
}
if (ptr == NULL)
{
fprintf(stderr, "CLI Server error: failed to bind\n\r");
exit(2);
}
freeaddrinfo(servinfo); // all done with this
rv_listen = listen(sockfd, 10);
////////////////////////////////////////////////////////////////////////////////////////////////
// UDP port setup
int sockfd_udp; // listening socket descriptor
struct sockaddr_storage remoteaddr_udp; // client address
socklen_t addrlen_udp;
char buf_udp[256]; // buffer for client data
char buf_copy_udp[256];
int recv_bytes_udp;
char remoteIP_udp[INET6_ADDRSTRLEN];
int yes_udp=1; // for setsockopt() SO_REUSEADDR
int j, rv_getaddrinfo_udp, rv_setsockopt_udp, rv_bind_udp;
struct addrinfo hints_udp, *servinfo_udp, *ptr_udp;
memset(&hints_udp, 0, sizeof(hints_udp));
hints_udp.ai_family = AF_UNSPEC;
hints_udp.ai_socktype = SOCK_DGRAM;
hints_udp.ai_flags = AI_PASSIVE;
rv_getaddrinfo_udp = getaddrinfo(NULL, "2001", &hints_udp, &servinfo_udp);
for(ptr_udp=servinfo_udp; ptr_udp!=NULL; ptr_udp=ptr_udp->ai_next)
{
sockfd_udp = socket(ptr_udp->ai_family, ptr_udp->ai_socktype, ptr_udp->ai_protocol);
rv_setsockopt_udp = setsockopt(sockfd_udp, SOL_SOCKET, SO_REUSEADDR, &yes_udp, sizeof(int));
rv_bind_udp = bind(sockfd_udp, ptr_udp->ai_addr, ptr_udp->ai_addrlen);
break;
}
if (ptr_udp == NULL)
{
fprintf(stderr, "CLI UDP Server error: failed to bind\n\r");
exit(2);
}
freeaddrinfo(servinfo_udp); // all done with this
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// add the listener to the master set
FD_SET(sockfd, &master);
FD_SET(sockfd_udp, &master);
// keep track of the biggest file descriptor
if(sockfd > sockfd_udp)
fdmax = sockfd; // so far, it's this one
else
fdmax = sockfd_udp; // so far, it's this one
do
{
read_fds = master; // copy it
rv_select = select(fdmax+1, &read_fds, NULL, NULL, &timeout);
// run through the existing connections looking for data to read
for(i=0; i<=fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{ // we got one!!
if (i == sockfd)
{
// handle new connections
addrlen = sizeof(remoteaddr);
newsockfd = accept(sockfd, (struct sockaddr *)&remoteaddr, &addrlen);
FD_SET(newsockfd, &master); // add to master set
if (newsockfd > fdmax) // keep track of the max
fdmax = newsockfd;
inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET6_ADDRSTRLEN);
fprintf(stdout, "CLI Server: new connection from %s on socket %d\n\r", remoteIP, newsockfd);
}
else if (i == sockfd_udp)
{
// handle new udp connections
addrlen_udp = sizeof(remoteaddr_udp);
recv_bytes_udp = recvfrom(i, buf_udp, sizeof(buf_udp), 0, (struct sockaddr *)&remoteaddr_udp, &addrlen_udp);
inet_ntop(remoteaddr_udp.ss_family, get_in_addr((struct sockaddr*)&remoteaddr_udp), remoteIP_udp, INET6_ADDRSTRLEN);
for(j=0; j<=recv_bytes_udp; j++)
{
if( (buf_udp[k] == '\r') | (buf_udp[k] == '\n') )
buf_udp[k] = '\0';
}
fprintf(stdout, "CLI UDP Server: received %s from connection %s\n\r", buf_udp, remoteIP_udp);
}
else
{ // handle data from a client
if ((recv_bytes = recv(i, buf_tcp, sizeof(buf_tcp), 0)) <= 0)
{ // got error or connection closed by client
if (recv_bytes == 0) // connection closed
{
fprintf(stdout, "CLI Server: socket %d hung up\n\r", i);
}
else
{
perror("CLI Server error: recv");
exit(6);
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
else
{
for(k=0; k<=recv_bytes; k++)
{
if( (buf_tcp[k] == '\r') | (buf_tcp[k] == '\n') )
buf_tcp[k] = '\0';
}
fprintf(stdout, "CLI Server: received %s from socket %d\n\r", buf_tcp, i);
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} while(QUIT);
I am doing error checking at each stage but didn't include it in the snippet. When I compile & run this, I can connect to port 2000 but not to 2001, my Tera term terminal closes with connection refused message. Why is the client not able to connect to port 2001 (UDP socket) but connects to port 2000 (TCP socket). The server just responds to client messages until client enters QUIT.
I have modeled this code from Beej's Guide to Network Programming selectserver.c code.
The program that I wrote above was correct but my understanding of socket programming was not. It was a fluke that I managed to write correct code but thanks to #EJP for having an extended discussion in the comments to clear up my doubt.
My mistake was using Teraterm's TCP client to connect to a UDP server. Both communications are mutually exclusive & hence can't communicate with one another. So I had to use a UDP client. Netcat provides an option of UDP client using netcat -u <ip address> <port>. Then my UDP server was able to receive messages from UDP client.
Another mistake was confusing bind() with connect() in DATAGRAM sockets. A connected DGRAM is when I use connect on both server & client.
I thought the problem was with select(), as I wrongly thought that UDP & TCP sockets can't be used simultaneously in select(). But the above code is how you write a UDP/TCP server for multiple client.
Thanks again to Beej & #EJP

setsockopt() get EBADF in mmaped netlink

Im trying to use memory map I/O netlink to transfer bulk packets from kernel to user space, and I followed a guide document from Patrick McHardy 1. However, when I try to setup the shared ring buffer in user space by using:
setsockopt(sock_fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req));
setsockopt(sock_fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req));
Both functions return -1, and the errno is 1, which means the descriptor is invalid. Im confused about that because I also referred to many other source codes and they can setup the ring successfully.
My code is almost the same as Patrick's 1:
int sock_fd = -1;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_DECODE);
if (sock_fd < 0)
return -1;
bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
/* init the mmap buffer */
unsigned int block_size = 16 * getpagesize();
struct nl_mmap_req req = {
.nm_block_size = block_size,
.nm_block_nr = 64,
.nm_frame_size = 16384,
.nm_frame_nr = 64 * block_size / 16384,
};
/* Configure ring parameters */
if (setsockopt(sock_fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0){
if(errno > 0)
printf("%d\n", errno);
}
if (setsockopt(sock_fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0){
if(errno > 0)
printf("%d", errno);
exit(1);
}
This code is built in Ubuntu 14.04, and kernel version is 3.13.0-74-generic.
Anyone idea about it? Thanks a lot.

modbus_read_register - Error connection timed out

We are using libmodbus library to read register values from energy meter EM6400 which supports Modbus over RTU. We are facing the following two issues.
1) We are facing an issue with modbus_read_registers API, this API returns -1 and the error message is:
ERROR Connection timed out: select.
After debugging the library, we found this issue is due to the echo of request bytes in the response message.
read() API call in _modbus_rtu_recv returns request bytes first followed by response bytes. As a result, length_to_read is calculated in compute_data_length_after_meta() based on the request bytes instead of response bytes (which contains the number of bytes read) and connection timed out issue occurs.
We tried to use both 3.0.6 and 3.1.2 libmodbus versions but same issue occurs in both the versions.
2) modbus_rtu_set_serial_mode (ctx, MODBUS_RTU_RS485) returns "BAD file descriptor".
Please confirm if there is any API call missing or any parameter is not set correctly.
Our sample code to read register value is as follows.
int main()
{
modbus_t *ctx;
uint16_t tab_reg[2] = {0,0};
float avgVLL = -1;;
int res = 0;
int rc;
int i;
struct timeval response_timeout;
uint32_t tv_sec = 0;
uint32_t tv_usec = 0;
response_timeout.tv_sec = 5;
response_timeout.tv_usec = 0;
ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'E', 8, 1);
if (NULL == ctx)
{
printf("Unable to create libmodbus context\n");
res = 1;
}
else
{
printf("created libmodbus context\n");
modbus_set_debug(ctx, TRUE);
//modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK |MODBUS_ERROR_RECOVERY_PROTOCOL);
rc = modbus_set_slave(ctx, 1);
printf("modbus_set_slave return: %d\n",rc);
if (rc != 0)
{
printf("modbus_set_slave: %s \n",modbus_strerror(errno));
}
/* Commented - Giving 'Bad File Descriptor' issue
rc = modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
printf("modbus_rtu_set_serial_mode: %d \n",rc);
if (rc != 0)
{
printf("modbus_rtu_set_serial_mode: %s \n",modbus_strerror(errno));
}
*/
// This code is for version 3.0.6
modbus_get_response_timeout(ctx, &response_timeout);
printf("Default response timeout:%ld sec %ld usec \n", response_timeout.tv_sec, response_timeout.tv_usec );
response_timeout.tv_sec = 60;
response_timeout.tv_usec = 0;
modbus_set_response_timeout(ctx, &response_timeout);
modbus_get_response_timeout(ctx, &response_timeout);
printf("Set response timeout:%ld sec %ld usec \n", response_timeout.tv_sec, response_timeout.tv_usec );
/* This code is for version 3.1.2
modbus_get_response_timeout(ctx, &tv_sec, &tv_usec);
printf("Default response timeout:%d sec %d usec \n",tv_sec,tv_usec );
tv_sec = 60;
tv_usec = 0;
modbus_set_response_timeout(ctx, tv_sec,tv_usec);
modbus_get_response_timeout(ctx, &tv_sec, &tv_usec);
printf("Set response timeout:%d sec %d usec \n",tv_sec,tv_usec );
*/
rc = modbus_connect(ctx);
printf("modbus_connect: %d \n",rc);
if (rc == -1) {
printf("Connection failed: %s\n", modbus_strerror(errno));
res = 1;
}
rc = modbus_read_registers(ctx, 3908, 2, tab_reg);
printf("modbus_read_registers: %d \n",rc);
if (rc == -1) {
printf("Read registers failed: %s\n", modbus_strerror(errno));
res = 1;
}
for (i=0; i < 2; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
avgVLL = modbus_get_float(tab_reg);
printf("Average Line to Line Voltage = %f\n", avgVLL);
modbus_close(ctx);
modbus_free(ctx);
}
}
Output of this sample is as follows:
created libmodbus context
modbus_set_slave return: 0
modbus_rtu_set_serial_mode: -1
modbus_rtu_set_serial_mode: Bad file descriptor
Default response timeout:0 sec 500000 usec
Set response timeout:60 sec 0 usec
Opening /dev/ttyUSB0 at 19200 bauds (E, 8, 1)
modbus_connect: 0
[01][03][0F][44][00][02][87][0A]
Waiting for a confirmation...
ERROR Connection timed out: select
<01><03><0F><44><00><02><87><0A><01><03><04><C4><5F><43><D4><C6><7E>modbus_read_registers: -1
Read registers failed: Connection timed out
reg[0]=0 (0x0)
reg[1]=0 (0x0)
Average Line to Line Voltage = 0.000000
Issue 1) is probably a hardware issue, with "local echo" enabled in your RS-485 adapter. Local echo is sometimes used to confirm sending of data bytes on the bus. You need to disable it, or find another RS-485 adapter.
I have written about this in the documentation of my MinimalModbus Python library: Local Echo
It lists a few common ways to disable local echo in RS-485 adapters.

cannot connect to lwip binded socket address after idle for sometime

I am using lwIP under FreeRTOS, the IP server runs on an ARM based device (Xilinx Zynq-7020) and the client is a windows.
At the server side, when I bind, listen, and accept an address:port, the client can connect to and read/write from this address. And after program exit, idle for some short time (10s~30s) then start again, it can also read/write from the server. Howeber, when I exit the client, and idle for a long time (>30s), I cannot connect to the server anymore. And at the server side, it no longer accept any address. Any solutions ?
The code for the server side is listed below:
void echo_server( void *pvParameters )
{
long lSocket, lClientFd, lAddrLen = sizeof( struct sockaddr_in );
struct sockaddr_in sLocalAddr;
struct sockaddr_in client_addr;
static char dIn[BUF_SIZE];
int ret;
( void ) pvParameters;
int on = 1;
int idle = 60;
int intvl = 15;
int cnt = 3;
/* step 1. create and setopts */
lSocket = lwip_socket(AF_INET, SOCK_STREAM, 0);
if( lSocket >= 0 )
{
lwip_setsockopt(lSocket, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
//lwip_setsockopt(lSocket, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
//lwip_setsockopt(lSocket, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl));
//lwip_setsockopt(lSocket, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr));
// prepare bind on port
sLocalAddr.sin_family = AF_INET;
sLocalAddr.sin_len = sizeof(sLocalAddr);
sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sLocalAddr.sin_port = ntohs( ( ( unsigned short ) ECHO_PORT ) );
/* step 2. bind */
if( lwip_bind( lSocket, ( struct sockaddr *) &sLocalAddr, sizeof( sLocalAddr ) ) < 0 )
{
lwip_close( lSocket );
vTaskDelete( NULL );
}
/* step 3. listen */
if( lwip_listen( lSocket, BACKLOG ) != 0 )
{
lwip_close( lSocket );
vTaskDelete( NULL );
}
for( ;; )
{
/* step 4. accept */
xil_printf("(ZYNQ-ECH) wait connection\n");
lClientFd = lwip_accept(lSocket, ( struct sockaddr * ) &client_addr, ( u32_t * ) &lAddrLen );
xil_printf("(ZYNQ-ECH) accept connection\n");
lwip_setsockopt(lClientFd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
//lwip_setsockopt(lClientFd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
//lwip_setsockopt(lClientFd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl));
//lwip_setsockopt(lClientFd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
// if client socket created
if( lClientFd > 0L )
{
int packet = 0;
while(1)
{
ret = mfeit_read(lClientFd, dIn);
if (ret == -1 || ret == 0)
break;
ret = mfeit_write( lClientFd, dIn, ret );
if (ret == -1 || ret == 0)
break;
packet ++;
}
xil_printf("(ZYNQ-ECH) close connection, total = %d\n", packet);
lwip_close( lClientFd );
}
}
}
/* Will only get here if a listening socket could not be created. */
xil_printf("(ZYNQ-ECH) task killed !!\n");
vTaskDelete( NULL );
}
I forgot about that stuff, but first you need to set up proper way of keep-alive.
is keep-alive on server configured, so for how long he is persisting keep-alive
set proper time for keep-alive on your side.
Don't do keep-alive with 2hrs or someting. Create a new socket for that it's bad for server resources.