Ping program implementation in C about recvfrom() doubts - c

I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this.
I have finished writing the program, and today when I tested the ping loopback address, after sending the packet, the function recvfrom() received the "first" packet (type 8), and the second recvfrom() received the response packet (type 0).
It was later discovered that the type value for the odd times was 8 and the type value for the even times was 0.
The actual packets I caught with Wireshark have corresponding response packets each time, but the first time received by recvfrom() are the outgoing packets.
// Send
if (sendto(sockfd, &sendicmp, ICMP_SIZE, 0, (struct sockaddr *) &to, sizeof(to)) == -1) {
printf("sendto() error \n");
continue;
}
// Receive
struct timeval timeout = {3, 0};//3s
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
if ((n = recvfrom(sockfd, buf, BUF_SIZE, 0, (struct sockaddr *) &from, &fromlen)) < 0) {
printf("Time out! \n");
continue;
}
nreceived++;
if (unpack(buf, n) == -1) {
printf("unpack() error \n");
}
enter image description here
Since the type value is not 0, I let the output is not the ICMP packet sent to me

An ICMP type 8 control message is an echo request. A type 0 is an echo reply. Thus, it sounds like your program is receiving its own requests in addition to the replies to those requests. That is natural if you are successfully pinging a loopback address, because that's how loopback works.
It's less of an issue for TCP and UDP, because these protocols provide a concept of ports on top plain IP to distinguish between different applications communicating via the same address. ICMP does not have that, so it is the responsibility of a process receiving ICMP messages to perform its own message filtering. A ping program in particular would probably ignore incoming ICMP messages other than echo replies (type 0). It might furthermore use the second four octets of the ICMP header to distinguish replies to its own echo requests from replies to other programs running at the same time.

Related

Raw socket send and receive

Just for the purpose of learning raw sockets in C I am writing a simple server that uses raw sockets to receive and send messages.
I create the socket
if ((r_sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP))< 0){
perror("socket");
exit(-1);
}
Then I create an infinite loop and start receiving, processing, and replying
while(1){
if((n = recvfrom(r_sock, buffer, BUFLEN, 0, (struct sockaddr *) &client, &client_len))<0){
perror("recvfrom");
exit(-1);
}
// Discard messages not intended to the server
if(htons(udp->uh_dport) != my_port){
continue;
}
//Do whatever with the data received and then send reply to client
// ....
if((n = sendto(r_sock, udp, ntohs(udp->uh_len), 0, (struct sockaddr *) &client, client_len))<0){
perror("sendto");
exit(-1);
}
}
I am not showing here the definition of every single variable but for the sake of completeness, buffer is a char array of size BUFLEN (big enough) and udp is a struct udphdr pointer to the right position in the buffer.
The point is that I have another program that serves as client using standard UDP sockets (SOCK_DGRAM) which is proved to be working properly (I also tried with netcat just in case). When I send a message with the client, it never receives the reply back. It seems that when the server sends the reply to the client, the server itself gets the message and the client gets nothing.
So, my question is: is there a way of solving this with raw sockets? That is, to make the server not receive its own messages and preventing others from receiving them?
Thanks in advance!
I have just realised that it was a problem with the checksum... Once I had a correct checksum in UDP the packet was correctly received by the client.
Wireshark gave me the lead to the solution. I saw that the checksum was not validated so I went to Edit > Preferences > Protocols > UDP > Validate the UDP checksum if possible and checked it.
Hope it helps

Send returns 29200 on ARM

I'm using a c program to respond to API calls. I want to reply using JSON.
I created a streaming socket listening on my port and create a GET request using a browser (firefox in my case). I then reply using the "send" method based on the request received.
The problem is when my reply is bigger than 29200 bytes. Then the send method returns 29200 and only sends the first 29200 bytes, then it just stops. I cannot find why it would stop at this number.
I tried google and found:
C++ socket programming Max size of TCP/IP socket Buffer?
My socket is blocking, so the send() function should block until all data is sent.
I also tried to find if linux blocks anything, but when I checked (not sure how I checked, cannot find the stackoverflow issue describing this) it was set to something way bigger than 29200.
I would like to know why my socket stops at 29200 and, if possible, how I can change the socket to make it send more data?
Edit:
Did some testing with the following results:
Created a test program to just send back 29999 bytes of data: https://pastebin.ca/4010317
I'm using curl to receive the data using
curl -X GET -i 'http://:12345'
When running on my computer the response is:
received: -1 bytes
received:
sent 29999 bytes
I can see that on my computer (x64) the receive does not work, but the send does (Curl does receive the data)
but when running on the ARM device the response is:
received: 83 bytes
received: GET / HTTP/1.1
Host: 192.168.1.118:12345
User-Agent: curl/7.47.0
Accept: */*
J
sent 29200 bytes
Here, curl receives 29200 bytes of data.
When trying to loop the send (https://pastebin.ca/4010318), the result is:
received: -1 bytes
received:
sent 29199 bytes
Here, curl receives 29200 bytes, the second send returns -1. So looping is not possible.
I will keep trying, but the help is appreciated.
Your code work.
Well, to be more exact, it work despite many little misusing function.
First, I asked you an mvce : you give a code with some useless line of code that add just complexity : if you don't need the information on the client connexion, the you can pass NULL to accept.
accept4(socketId, NULL, NULL, SOCK_NONBLOCK);
This way, we do not have 2 useless variables.
Second : Checking for error is cool, but displaying something (or logging) is better, because you can have hint why this doesn't work.
For example, the "received: -1 bytes" that you interpret as non-working is in fact working : The error (errno) is EAGAIN, meaning that since your socket is non-blocking, the data is not currently available so you have to loop your recv to read the incomming data. Looping recv will "solve" your false problem.
And finally : No, you do not loop your send either : you just merely detect if you haven't send all the data and try again one more time : do a LOOP !
Edit :
You can see how I do your "initServerSocket" function for the "check error and logging" part :
int InitServerSocket(int portNum, int nbClientMax)
{
int socketId = -1;
struct sockaddr_in addressInfo;
int returnFunction = -1;
if ((socketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
// Log !
goto END_FUNCTION;
}
addressInfo.sin_family = AF_INET;
addressInfo.sin_addr.s_addr = htonl(INADDR_ANY);
addressInfo.sin_port = htons(portNum);
if (bind(socketId, (struct sockaddr *)&addressInfo, sizeof(addressInfo)) == -1) {
// Log !
goto END_FUNCTION;
}
const int flags = fcntl(socketId, F_GETFL, 0);
fcntl(socketId, F_SETFL, flags ^ O_NONBLOCK);
// Error detection & log ?
if (listen(socketId, nbClientMax) == -1) {
// Log !
goto END_FUNCTION;
}
returnFunction = socketId;
socketId = -1;
/* GOTO */END_FUNCTION:
if (socketId != -1) {
close(socketId);
}
return (returnFunction);
}
I finally found the issue, the problem was that the
accept4(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo, SOCK_NONBLOCK);
was setting the socket to non blocking, while in the init the socket was set to blocking. On the ARM device, this caused the "write" function to stop writing after 29200 bytes (not sure why).
When i changed the accept4 to:
accept(socket, (struct sockaddr *) &addrInfoFromClient, &sizeAddrInfo);
It worked.

Multicast echo server

This is more of a conceptual confusion. I am making a multicast server which just echoes back the datagram received.Here's the code
while (1) {
cnt = recvfrom(sock, message, sizeof(message), 0,
(struct sockaddr *) &addr, &addrlen);
//printf("%d \n",cnt);
if (cnt < 0) {
perror("recvfrom");
exit(1);
} else if (cnt == 0) {
break;
}
printf("%s: message = \"%s\"\n", inet_ntoa(addr.sin_addr), message);
addr.sin_addr.s_addr = inet_addr(EXAMPLE_GROUP);
cnt = sendto(sock, message, sizeof(message), 0,
(struct sockaddr *) &addr, addrlen);
if (cnt < 0) {
perror("sendto");
exit(1);
}
}
The problem with this is as a multicast server will also receive the datagram. So, after it recieves a datagram, it sends, it again recieves the same datagram, and so on entering an infinite loop. Any pointers on how to implement such type of server?
You need to disable multicast loopback via setsockopt().
One option is what EJP said, to disable multicast loopback so that the sending machine doesn't receive a copy of its own multicast packet back. However, be aware that if you do that, you won't be able to test with clients and server all running on the same machine anymore, since IP_MULTICAST_LOOP is implemented at the IP routing level.
A second possible option to avoid infinite packet loops would be to have the echo-server send its response packets to a different multicast group than the one it listens on (or even have it send its responses via unicast rather than multicast; the server could call recvfrom() to find out the unicast source address of any packet it receives, so it's easy for it to know where to send the reply packet back to)
A third option would be to modify the contents of the packet somehow to mark it as already-seen, so that your server knows not to echo it a second time. For example, you could specify that your server will only echo packets whose first byte is set to zero, and when your server echoes a packet, it makes sure to set the packet's first byte to one before send()-ing it out. (Your clients would need to be aware of this convention, of course)

rawsocket sendto() some of the packet are dropped and not seen in the network

socketFd_ = socket(AF_INET, SOCK_RAW, protoType);
sentBytes = sendto(socketFd_, buf, len, 0,
(struct sockaddr *)&sa,sizeof(structsockaddr_in));
protoType = GRE
I am sending the 1000 packets in the network.
If my tx packet rate is 40, i am able to see all the packet in wireshark.
however when i will try to send at the rate of 100 some of the packet(3-4) will not reach in the network however sendto did not return any error.
i know sendto will just put the txpacket into the queue and will not guarantee the delivery of packet in the network however from where i can get the drop packet statistics and reason for packet drop in the kernel.
i have tried increasing the txqueuelen of interface to 65000 but it did not helped.
how can i debug this issue?.
You are correct that sendto will just put the txpacket in the queue and not guarantee delivery.
From the iEEE documentation on sendto:
Successful completion of a call to sendto() does not guarantee delivery of the message. A return value of -1 indicates only locally-detected errors.
You have to perform some of your own throttling via ioctl() and getsockopt() as you are almost doing. I don't think you can expect the network stack to do it all for you.
Something like:
sendMsgThrottled( msg Msg ) {
ioctl(socketFd_, SIOCOUTQ, &outstandingBytes);
if ( outStandingBytes < limit )
sendto(socketFd_, ..)
else
queueMsg(); /* Or delay */
}
I would test this with by having the send and receiver on the same machine, packet loss can come from other places besides the network layer of the operating system.

I set SO_RCVLOWAT (Receive Low Water Mark ) option on a UDP socket but it doesn't work

I am using C network programming to set the socket option SO_RCVLOWAT on a UDP socket. I succeeded as shown by the return value of getsockopt(). The problem: I can still receive any data sizes greater than zero.
Example: I set the socket option SO_RCVLOWAT value to 1024 byte on the client side and server sent 256 byte to the client. It can receive the 256 byte, but it actually shouldn't receive this message because the receive low water mark is 1024 byte.
Relevant code:
rc = setsockopt(sd, SOL_SOCKET, SO_RCVLOWAT, (char *)&recvlowat, sizeof(recvlowat));
if(rc < 0){
VL_MISC_ERR(("Setting SO_RCVLOWAT option error, %s",strerror(errno)));
return -1;
}
sd : a valid file descriptor
Then I use recvfrom:
c = recvfrom(sd, databuf, datalen, 0, (struct sockaddr_in *)&localSock, &addrlen);
It isn't supposed to have that effect in UDP. recvfrom() receives one datagram at a time, period. If you want to receive more than one at a time, use recvmsg().

Resources