Search code examples
csocketsipraw-socketsrecvfrom

Why does recvfrom create additional IP header?


Why does recvfrom create additional IP header?

I am sending:

-------------
- IP header -
-------------
-   Data   -
-------------

But when I try to receive data, it looks like there is

-------------
- IP header -
-------------
- IP header -
-------------    
-   Data   -
-------------

Client code:

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netinet/ip.h>

#define DEBUG 1


int main(void) {

    // Create File descrptor for socket
    int socket_fd;
    if ((socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_IPV6)) < 0) {
        perror("ERROR Socket");
        exit(2);
    }

    char *srcIP = "127.0.0.1";
    char *dstIP = "127.0.0.1";

    // Create address structure
    struct sockaddr_in daddr;
    daddr.sin_family = AF_INET;
    daddr.sin_port = 0; // Raw sockets can't use ports
    inet_pton(AF_INET, "127.0.0.1", &daddr.sin_addr);
    memset(daddr.sin_zero, 0, sizeof(daddr.sin_zero));

    // Create a Packet
    char packet[50];
    memset(packet, 0, sizeof(packet));

    // Structure packet
    struct iphdr *ip_header = (struct iphdr *) packet;
    // Data to be appended at the end of the ip header
    char *data = (char *) (packet + (sizeof(struct iphdr)));
    ip_header->version = 4; // IPv4
    ip_header->tos = 0; // Type of service
    ip_header->ihl = 5; // 5 x 32-bit words
//    ip_header->tot_len = htons(sizeof(struct iphdr) + strlen(data)); // Total IP packet length
    ip_header->tot_len = sizeof(packet); // Total IP packet length
    ip_header->protocol = IPPROTO_IPV6; // 6in4 protocol
    ip_header->frag_off = 0x00; //16 bit field = [0:2] flags + [3:15] offset = 0x0
    ip_header->ttl = 0xFF; // Max number of hops 16bit
//    ip_header->id = htons(54321); // 0x00; //16 bit id
    ip_header->check = 0;  //16 bit checksum of IP header. Can't calculate at this point
    ip_header->saddr = inet_addr(srcIP);
    ip_header->daddr = inet_addr(dstIP);
    data[0] = 'T';
    data[1] = 'E';
    data[2] = 'S';
    data[3] = 'T';
    data[4] = '7';
    data[5] = '\0';

//    ip_header->check = csum((unsigned short *) packet, ip_header->tot_len);
#if DEBUG
    printf("\nIP header checksum: %d\n", ip_header->check);
#endif

    while (1) {
        sleep(1);
        if (sendto(socket_fd, (char *) packet, sizeof(packet), 0,
                   (struct sockaddr *) &daddr, (socklen_t) sizeof(daddr)) < 0)
            perror("Packet send error");
    }

    return 0;
}

Server code:

/*** IPPROTO_RAW receiver ***/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int socket_fd;
    struct sockaddr_in saddr;
    char packet[50];

    if ((socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_IPV6)) < 0) {
        perror("socket_fd");
        exit(EXIT_FAILURE);
    }

    memset(packet, 0, sizeof(packet));
    socklen_t len = (socklen_t) sizeof(saddr);

    while(1) {
        if (recvfrom(socket_fd, packet, sizeof(packet), 0,
                     (struct sockaddr *)&saddr, &len) < 0)
            perror("recvfrom");

        int i = sizeof(struct iphdr);   ////// WHY IS DATA PAYLOAD ON packet[sizeof(struct iphdr) * 2]
        while (i < sizeof(packet)) {
            fprintf(stderr, "%c", packet[i]);
            i++;
        }
        printf("\n");
    }
    exit(EXIT_SUCCESS);
}

Solution

  • In your sender, you need to set the IP_HDRINCL socket option. That tells the API that you're manually supplying the IP header. Because you're not setting this option, you're effectively putting your own copy of the IP header after the one the system adds.