2

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.

mpontillo
  • 13,559
  • 7
  • 62
  • 90
Matt K
  • 598
  • 5
  • 19
  • 3
    Please show your actual code. Is it possible that there is other data waiting to be read from the socket? AFAIK, there is no way to tell `poll()` to only look for ARP messages and ignore everything else. When `poll()` reports data is ready to read, what does `recvfrom()` actually return? – Remy Lebeau Feb 13 '14 at 00:14
  • ... and are you using `MSG_PEEK` by any chance? – user207421 Feb 13 '14 at 00:55
  • I added a stripped down version of my code with all of the same socket setup – Matt K Feb 13 '14 at 17:12
  • Your code seems to be working for me on RHEL 6. – mpontillo Feb 13 '14 at 17:29
  • Thanks for the additional confirmation. I'm not sure if there's some generalization in my code that causes it to break with the ARM GCC compiler. As a random side note, I changed `recfrom()` to `read()` since I'm not utilizing any network stuff in my call and the behaviour is the same on the Beaglebone – Matt K Feb 13 '14 at 17:31
  • I think it's more likely to be a kernel or libc issue. Or perhaps there is some subtlety about the platform that you haven't accounted for. Have you tried using `libpcap`? Does it exhibit the same issue? – mpontillo Feb 13 '14 at 17:31

1 Answers1

1

I think the following might be a reason * dont set revents in poll.It is actullay an output parameter * Use some positive value in timeout. If you want infinite wait time use -1. Refer Man page of poll