setsockopt() get EBADF in mmaped netlink - sockets

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.

Related

C: Server/client socket program - Client error connecting

I'm new to networking and trying to create a simple client, server socket program in C, where arguments determine whether the program should run as a client or server. I did this by using simple if statements (if a flag is given, run as server, else run as client), but I'm not sure how to test this. I run my code with the argument to be a server in one terminal (on localhost and port number 3000 for example), and open another terminal and run the code with the argument to be a client (also on localhost and the same port).
The expected result is to see the client prompt the user for a message (if connected successfully), and send that message to the server, which prints out the message, however, I don't get the prompt on the client terminal to enter a message.
(I got the code for server and client behavior from one of many websites online, but they separate the client.c and server.c, whereas I want to combine both into one .c program)
Here's my code below, the error is triggered by
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
in the client section of the code.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
void error(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n, i, server = 0; // 1 = server, 0 = client
// check if server or client
for (i = 0; i<argc; i++) {
if (strcmp(argv[i], "-l") == 0)
server = 1;
}
// client
if (server == 0) {
struct hostent *server;
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"FOUR*** ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
return 0;
}
// server
if (server == 1) {
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
return 0;
}
}
Here's the exact error output:
In one terminal window, I run the program as a server first:
$ ./socketz -l localhost 2003
Then in another terminal window, I run the program as a client:
$ ./socketz localhost 2003
ERROR connecting: Connection refused
The reason you can't connect is because your server process is not listening on port 2003. In particular, on this line:
serv_addr.sin_port = htons(portno);
The value of portno is zero, which causes the value of serv_addr.sin_port to also be zero, which accept() interprets as meaning that it should just pick an available TCP port to bind to.
The root of the problem is here:
portno = atoi(argv[1]);
... that line assigns a value to portno based on the first argument you entered when running the program, but you entered this:
./a.out -l localhost 2003
So the first argument is "-l", which is a non-number so it will cause atoi() to return 0. I think what you intended was portno = atoi(argv[3]); instead.

Linux TCP socket timestamping option

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

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.

ICMP packet obsolete or malformed

I'm trying to use RAW sockets to create a ping program. The program I wrote however works only for localhost(127.0.0.1) and does not work for other IP addresses. While analyzing the generated packet in Wireshark, I get a message saying "Unknown ICMP (obsolete or malformed?)". It also tells me that the ICMP checksum is incorrect. I'm posting my code here
char source[20], destination[20];
struct sockaddr_in src, dst;
int addrlen= sizeof(src), recvSize, packetSize;
char *packet;
char *buffer;
struct iphdr* ip;
struct iphdr* ip_reply;
struct icmphdr* icmp;
unsigned short csum(unsigned short *, int);
char* getip();
int main(int argc, char* argv[]){
int pingSocket, optval;
struct protoent *protocol;
if(*(argv + 1) && (!(*(argv + 2)))){
//only one argument provided,assume it is the destination server
strncpy(destination, *(argv + 1), 15);
strncpy(source, getip(), 15);
}
inet_pton(AF_INET, destination, &(dst.sin_addr));
inet_pton(AF_INET, source, &(src.sin_addr));
protocol= getprotobyname("ICMP");
printf("Protocol number for ICMP is %d\n",protocol->p_proto);
pingSocket= socket(AF_INET, SOCK_RAW, protocol->p_proto); // Create socket
if(pingSocket< 0){
perror("Error creating socket: ");
exit(3);
}
printf("Socket created with identifier %d\n", pingSocket);
packetSize= sizeof(struct iphdr) + sizeof(struct icmphdr);
packet = (char *) malloc(packetSize);
buffer = (char *) malloc(packetSize);
ip= (struct iphdr*) packet;
icmp= (struct icmphdr*) (packet+ sizeof(struct iphdr));
memset(packet, 0, packetSize);
//Fill up the IP header
ip->ihl= 5;
ip->version= 4;
ip->tos= 0;
ip->tot_len = htons(packetSize);
ip->id = htons(0);
ip->frag_off= 0;
ip->ttl = 255;
ip->protocol= protocol->p_proto;
ip->saddr= src.sin_addr.s_addr;
ip->daddr= dst.sin_addr.s_addr;
setsockopt(pingSocket, protocol->p_proto, IP_HDRINCL, &optval, sizeof(int)); //HDRINCL to tell the kernel that the header is already included
icmp->type= ICMP_ECHO;
icmp->code= 0;
icmp->un.echo.id= rand();
icmp->un.echo.sequence= rand();
icmp->checksum= 0;
icmp->checksum = csum((unsigned short *)icmp, sizeof(struct icmphdr));
dst.sin_family= AF_INET;
sendto(pingSocket, packet, packetSize, 0, (struct sockaddr *)&dst, sizeof(dst));
printf("Sent ping request with size %d to %s\n", ip->tot_len, destination);
printf("Request's IP ID: %d and IP TTL: %d\n", ip->id, ip->ttl);
printf("Packet: \n%s\n", packet);
// Wait for response
if( (recvSize= recvfrom(pingSocket, buffer, sizeof(struct iphdr)+sizeof(struct icmphdr), 0, (struct sockaddr *)&dst, &addrlen)) < 0){
perror("Receive error: ");
}
else{
printf("Received a reply from %s of size %d\n", destination, recvSize);
ip_reply= (struct iphdr*) buffer;
printf("Reply's IP ID: %d and IP TTL: %d\n", ip_reply->id, ip_reply->ttl);
}
free(packet);
free(buffer);
close(pingSocket);
return 0;
}
unsigned short csum(unsigned short *ptr, int nbytes)
{
register long sum;
u_short oddbyte;
register u_short answer;
sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
if (nbytes == 1) {
oddbyte = 0;
*((u_char *) & oddbyte) = *(u_char *) ptr;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
char* getip()
{
char buffer[256];
struct hostent* h;
gethostname(buffer, 256);
h = gethostbyname(buffer);
return inet_ntoa(*(struct in_addr *)h->h_addr);
}
I read somewhere that someone fixed this issue by setting the ICMP checksum to Zero first and then calculating it. This however doesn't work for me. Please help me out :)

socket bad file descriptor

I want to write a multi clients socket program,
but I get Bad file descriptor when the stage of accept.
How can I correct my code? Thanks!
Here is my code
http://codepad.org/q0N1jTgz
Thanks!
Here is my part of code!
while(1)
{
struct sockaddr_in client_addr;
int addrlen = sizeof(client_addr);
/*Accept*/
if(clientfd = accept(sockfd, (struct sockaddr *)&client_addr, (socklen_t*)&addrlen) < 0)
{
perror("Accpet Error");
close(sockfd);
exit(-1);
}
/*Fork process*/
if(child = fork() < 0)
{
perror("Fork Error");
close(sockfd);
exit(-1);
}
else if(child == 0)
{
int my_client = clientfd;
close(sockfd);
send(my_client, welcome, sizeof(welcome), 0);
while ((res = recv(my_client, buffer1, sizeof(buffer1), 0)) > 0)
{
string command(buffer1);
cout << "receive from client:" << command << ", " << res << " bytes\n";
memset(buffer1, '\0', sizeof(buffer1));
}
}
close(clientfd);
}
there are a few bugs in your code
first you need to use parentheses around the assignments for child and clientfd.
line 68 should be changed to
if((clientfd = accept(sockfd, (struct sockaddr *)&client_addr, (socklen_t*)&addrlen)) < 0)
and line 76 should be
if((child = fork()) < 0)
additionally you must return or exit() from the forked process since you have already closed the listening socket.
so add exit(0); or return 0; after line 94
I highly recommend you compile your code with warnings enabled, to catch the assignment problems early. e.g use the -Wall and -Wextra flags if you are using gcc or g++