Search code examples
clinuxsocketsudpraw-sockets

C raw UDP socket failing to receive sent data using recvfrom() on loopback address


I have 2 programs. One program sends data to the other program. Both programs use raw UDP sockets. The sending program sends to loopback address 127.0.0.1, from port 25252 to port 80. The receiving program attempts to receive any incoming data using recvfrom().

The sending program's packets do not appear in Wireshark. The receiving program does not receive any packets I try sending it with Netcat. I am unsure of where I'm going wrong. I'd like to stick to using automatically generated IP headers, and only creating my own UDP headers.

The 2 programs are supposed to be scaled-down versions of programs I'm currently trying to debug. There, the sending program actually works and its traffic can be seen in Wireshark and received by Netcat. The receiving program, however, doesn't work there, either. I am unsure of where I'm going wrong and why my results are so inconsistent. I'm aware that recvfrom() will capture all traffic, so for the sake of this example, assume that the only traffic on the network originates from the sending program.

I'm looking for what I need to change about these 2 programs to make the communication successful, as well as general advice on what I'm doing wrong. Below is the code for both programs (excluding #includes).

Server:

int main() {

    //Make sock
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sock == -1) {printf("sock error.\n"); return 1;}
    int fcntl_ret = fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
    if (fcntl_ret == -1) {printf("fcntl error.\n"); return 1;}

    //Make addr
    struct sockaddr_in addr;

    //Make body
    char packet[128] = {0};
    char * body = packet + sizeof(struct iphdr) + sizeof(struct udphdr);

    //Make udp header
    struct udphdr * header;
    header = (struct udphdr *) packet + sizeof(struct iphdr);

    //Receive
    ssize_t bytes;
    socklen_t len;
    while (1) {

        sleep(1);
        memset(packet, 0, 128);
        bytes = recvfrom(sock, packet, 128, 0, (struct sockaddr *)&addr, &len);
        header = (struct udphdr *) packet + sizeof(struct iphdr);

        printf("Checksum: %u\nBody: %s\n", header->check, body);


    }

    return 0;
}

Client:

int main() {

    //Make sock
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sock == -1) {printf("sock fail\n"); return -1;}

    //Make addr
    struct sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(80);
    addr.sin_family = AF_INET;

    //Make body
    char packet[128] = {0};
    char * body;
    body = (char *) (packet + sizeof(struct udphdr));
    memcpy(body, "test", 4);

    //Make udp header
    struct udphdr * header;
    header = (struct udphdr *) (packet + sizeof(struct iphdr));
    header->source = htons(25252);
    header->dest = htons(80);
    header->len = 128;
    header->check = 4;

    //Send
    ssize_t bytes;
    while (1) {

        sleep(1);
        bytes = sendto(sock, packet, 128, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); 
        printf("Sent %ld bytes.\n", bytes);
    }

    return 0;

}

Solution

  • On the client side, this line is wrong:

    header = (struct udphdr *) (packet + sizeof(struct iphdr));
    

    You said that you don't want to send your own IP header (and you are not enabling the IP_HDRINCL option to allow that), so your client should not be accounting for struct iphdr at all. You are creating the UDP header in the wrong portion of the packet's memory.

    Change that line to this instead:

    header = (struct udphdr *) packet;
    

    This change would also be consistent with your client's code that expects the packet to begin with a struct udphdr when assigning the body pointer.


    I would suggest restructuring your code to setup the pointers a little differently:

    Server:

    ...
    
    char packet[128] = {0};
    
    struct iphdr * iphdr = (struct iphdr *) packet;
    struct udphdr * header = (struct udphdr *) (iphdr + 1);
    char * body = (char *) (header + 1);
    
    ...
    

    Client:

    ...
    
    char packet[128] = {0};
    
    struct udphdr * header = (struct udphdr *) packet;
    char * body = (char *) (header + 1);
    
    ...