Raw socket sendto failed using C on Linux

Jeff picture Jeff · Sep 17, 2010 · Viewed 11.1k times · Source

I'm trying to send a raw packet using UDP, with the IP and UDP headers that I have constructed in my code. Raw packet successfully initialized with socket(PF_INET, SOCK_RAW, IPPROTO_UDP) and socket option set using setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)).

The problem is when I send the packet using sendto(), I get the error 'Message too long'.

My IP header is 20 bytes and UDP header 8 bytes and my data is 12 bytes. So the total is only 40 bytes. This can't possibly be too long for a single UDP packet. Can someone help?

The type of val is a pointer to an int

Here is my code:

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include <netinet/in.h>

//The packet length in byes
#define PCKT_LEN 50

//Date size in bytes
#define DATA_SIZE 12

//PseudoHeader struct used to calculate UDP checksum.
typedef struct PseudoHeader{    
    unsigned long int source_ip;
    unsigned long int dest_ip;
    unsigned char reserved;
    unsigned char protocol;
    unsigned short int udp_length;
}PseudoHeader;

// Ripped from Richard Stevans Book
unsigned short ComputeChecksum(unsigned char *data, int len)
{
    long sum = 0;  /* assume 32 bit long, 16 bit short */
    unsigned short *temp = (unsigned short *)data;

    while(len > 1){
        sum += *temp++;
        if(sum & 0x80000000)   /* if high order bit set, fold */
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }

    if(len)       /* take care of left over byte */
        sum += (unsigned short) *((unsigned char *)temp);

    while(sum>>16)
        sum = (sum & 0xFFFF) + (sum >> 16);

    return ~sum;
}

int BindRawSocketToInterface(int rawsock, char *addr, short int port)
{
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = inet_addr(addr);
    s_addr.sin_port = htons(port);

    if((bind(rawsock, (struct sockaddr *)&s_addr, sizeof(s_addr)))== -1)
    {
        perror("Error binding raw socket to interface\n");
        exit(-1);
    }

    return 1;
}


// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
struct ip *CreateIPHeader(char *srcip, char *destip)
{
    struct ip *ip_header;

    ip_header = (struct ip *)malloc(sizeof(struct ip));

    ip_header->ip_v = 4;
    ip_header->ip_hl = 5;
    ip_header->ip_tos = 0;
    ip_header->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
    ip_header->ip_id = htons(111);
    ip_header->ip_off = 0;
    ip_header->ip_ttl = 111;
    ip_header->ip_p = IPPROTO_TCP;
    ip_header->ip_sum = 0; /* We will calculate the checksum later */
    inet_pton(AF_INET, srcip, &ip_header->ip_src);
    inet_pton(AF_INET, destip, &ip_header->ip_dst);

    /* Calculate the IP checksum now : 
    The IP Checksum is only over the IP header */
    ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);

    return (ip_header);
}

// Creates a the UDP header.
struct udphdr *CreateUdpHeader(char *srcport, char *destport )
{
    struct udphdr *udp_header;

    /* Check netinet/udp.h for header definiation */

    udp_header = (struct udphdr *)malloc(sizeof(struct udphdr));

    udp_header->source = htons(atoi(srcport));
    udp_header->dest = htons(atoi(destport));
    udp_header->len = htons(sizeof(struct udphdr) + DATA_SIZE); //TODO: need to specify this
    udp_header->check = htons(0);

    return (udp_header);
}

void CreatePseudoHeaderAndComputeUdpChecksum(struct udphdr *udp_header, struct ip *ip_header, unsigned char *data)
{
    /*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/

    /* Find the size of the TCP Header + Data */
    int segment_len = ntohs(ip_header->ip_len) - ip_header->ip_hl*4; 

    /* Total length over which TCP checksum will be computed */
    int header_len = sizeof(PseudoHeader) + segment_len;

    /* Allocate the memory */

    unsigned char *hdr = (unsigned char *)malloc(header_len);

    /* Fill in the pseudo header first */

    PseudoHeader *pseudo_header = (PseudoHeader *)hdr;

    pseudo_header->source_ip = ip_header->ip_src.s_addr;
    pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
    pseudo_header->reserved = 0;
    pseudo_header->protocol = ip_header->ip_p;
    pseudo_header->udp_length = htons(segment_len);


    /* Now copy TCP */

    memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);

    /* Now copy the Data */

    memcpy((hdr + sizeof(PseudoHeader) + 8), data, DATA_SIZE);

    /* Calculate the Checksum */

    udp_header->check = ComputeChecksum(hdr, header_len);

    /* Free the PseudoHeader */
    free(hdr);
}

// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
    int sd;
    char buffer[PCKT_LEN];
    char *data = "Hello World!";

    // Source and destination addresses: IP and port
    struct sockaddr_in to_addr;
    int one = 1;
    const int *val = &one;

    printf("IP Header Size: %lu \n", sizeof(struct ip));
    printf("UDP Header Size: %lu \n", sizeof(struct udphdr));
    printf("Data Size: %d\n", DATA_SIZE);
    printf("IP Total: %lu \n", sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);

    memset(buffer, 0, PCKT_LEN);

    if(argc != 5)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
        exit(-1);
    }

    // Create a raw socket with UDP protocol
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }
    else
        printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");

    //Bind the socket to the source address and port.
    BindRawSocketToInterface(sd, argv[1], atoi(argv[2]));

    // Inform the kernel do not fill up the packet structure. we will build our own...
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) < 0)
    {
        perror("setsockopt() error");
        close(sd);
        exit(-1);
    }
    else
        printf("setsockopt() is OK.\n");


    // The source is redundant, may be used later if needed
    // The address family
    to_addr.sin_family = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr(argv[3]);
    to_addr.sin_port = htons(atoi(argv[4]));

    //Create the IP header.
    struct ip *ip_header = CreateIPHeader(argv[1], argv[3]);
    //Create the UDP header.
    struct udphdr *udp_header = CreateUdpHeader(argv[2], argv[4]);
    //Compute UDP checksum
    CreatePseudoHeaderAndComputeUdpChecksum(udp_header, ip_header, (unsigned char*)data);

    //Copy IP header, UDP header, and data to the packet buffer.
    memcpy(buffer, ip_header, sizeof(struct ip));
    memcpy(buffer + sizeof(struct ip), udp_header, 8 /*sizeof(struct udphdr)*/);
    memcpy(buffer + sizeof(struct ip) + 8, data, DATA_SIZE);

    printf("Using raw socket and UDP protocol\n");
    printf("Using Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));


    if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
    {
        perror("sendto() error");
    }
    else
    {
        printf("sendto() is OK.\n");
    }

    free(ip_header);
    free(udp_header);
    close(sd);
    return 0;
}

Answer

nos picture nos · Sep 17, 2010
if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)

The length here is wrong, you should not specify that in network byte order.

A simple 20 is enough. htons(20) will be a very large number on a little endian machine. ( if you want to send other things that just the IP header, you should include that too in the length,, sounds like your buffer is 40 bytes, not 20)