I'm trying to implement a protocol in C that sends/receives raw Ethernet frames, and I've ran into some problems using poll()
and recvfrom()
in a Linux environment. I think my problem is mostly conceptual, so I'll avoid posting my code for now.
I have two sockets for incoming data, and poll()
is being used to indicate when data is ready on either socket through its return value and the state of the associated pollfd
structure. This is working for me right now when I first run the program. For the sake of testing I'm only interested in ARP frames, and I'm able to use poll()
to wait for an ARP frame to arrive. When it arrives, I call recvfrom()
to copy the data into a location where I can process it. All of this is working fine.
The problem is that subsequent calls to poll()
continue to report that data is ready to be read from the socket even when no new data has arrived in the socket. Once I've called recvfrom()
and read the data from the socket, I'd like poll()
to wait until a new frames arrive before reporting that data is ready. I've never used poll()
before, so I'm not sure if there is an explicit step needed to "clear" the descriptor so that it will stop causing poll()
to report data being ready until a new frame comes in. I'm clearing the revents
member of the pollfd
structures before calling poll()
, but each call to poll()
is setting the value of revents
back to 1.
I've looked through the poll()
man pages and I haven't had any luck finding information on this. I feel like I'm misunderstanding how sockets/polling works at a high level, so any help would be appreciated.
[edit] Here's the majority of my code. I've actually simplified the code for this example by including only a single socket. All of this is also encapsulated in classes and spread out, but re-writing it like this reproduces my problems. My final code has error-checking on the API calls and copies my receive buffer to process the frame rather than just printing the contents, but other than that it's pretty much the same.
#include <stdio.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <poll.h>
#include <linux/if_ether.h>
int main()
{
uint8_t sendBuffer_[1536];
uint8_t receiveBuffer_[1536];
struct pollfd pollStruct[1];
printf("Making socket...");
int mySocket = socket( AF_PACKET, SOCK_RAW, htons(ETH_P_ARP) );
fcntl(mySocket, F_SETFL, O_NONBLOCK);
char *options;
options = "eth0";
setsockopt(mySocket, SOL_SOCKET, SO_BINDTODEVICE, options, 4);
printf("Descriptor: %i\n", mySocket);
while(1)
{
pollStruct[0].fd = mySocket;
pollStruct[0].events = POLLIN;
pollStruct[0].revents = 0;
if( poll(pollStruct, 1, 0) == 1)
{
printf("New Frame Arrived: ");
uint16_t frameLength = recvfrom( mySocket, receiveBuffer_, 1536, 0, NULL, NULL);
printf("Destination MAC: ");
for ( uint8_t i = 0; i < 6; ++i ) {
printf("0x%x ", receiveBuffer_[i]);
}
printf("\nSource MAC: ");
for ( uint8_t i = 0; i < 6; ++i ) {
printf("0x%x ", receiveBuffer_[i + 6]);
}
printf("\nEtherType: ");
for ( uint8_t i = 0; i < 2; ++i ) {
printf("0x%x ", receiveBuffer_[i + 12]);
}
printf("\nPayload: ");
for ( uint32_t i = 14; i < frameLength; ++i ) {
printf("0x%x ", receiveBuffer_[i]);
}
printf("\n");
//Clear buffer
for ( uint32_t i = 0; i < frameLength; ++i )
receiveBuffer_[i] = 0;
}
}
}
I didn't mention this originally, but I'm trying to deploy this code on a Beaglebone Black. When I run this program in a VM with Ubuntu compiled using GCC, it works as I'd expect. After receiving/printing a frame, the program visually idles until a new one arrives. When I cross-compile the program and run it, the program continuously prints the same packet until a new one arrives. Since I'm clearing receiverBuffer[]
whenever recvfrom()
returns, recvfrom()
doesn't seem to be clearing the data from its internal buffer once it's copied into my buffer, since it is continuously copying the data.