0

I made a C program that basically sends UDP packets to a destination on Linux (Ubuntu 18.04). I am trying to send out packets larger than the network interface's MTU size (and therefore, should be fragmented). However, whenever I attempt to do so, the sendto() function returns -1 and the packet is never sent. I am also specifying my own IP + UDP headers. From what I found online, fragmentation should be handled automatically even when passing your own IP and UDP headers. Although I wasn't able to find many others with similar issues. I was thinking that maybe I had to do a while loop and check how much data sendto() actually sends out. However, as I said before, the function returns -1 every time only when sending packets higher than the MTU size.

I would like to note I am also not binding the socket.

Here are what my IP and UDP headers look like currently:

// Fill out IP and UDP headers.
iphdr->ihl = 5;
iphdr->frag_off = 0;
iphdr->version = 4;
iphdr->protocol = IPPROTO_UDP;
iphdr->tos = 16;
iphdr->ttl = 64;
iphdr->id = 0;
iphdr->check = 0;
iphdr->saddr = inet_addr(con->sIP);
iphdr->daddr = inet_addr(con->dIP);
iphdr->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + len;

udphdr->uh_dport = htons(con->dPort);
udphdr->uh_sport = htons(port);
udphdr->len = htons(sizeof(struct udphdr) + len);
udphdr->check = 0;

iphdr->check = ip_csum((unsigned short *)buffer, sizeof(struct iphdr));

I also plan to calculate the UDP checksum (I was planning to do this after fixing the fragmentation issue).

Here's the code I used to create the socket:

// Create socket.
int sockfd, one = 1;

sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);

// Check for socket error.
if (sockfd <= 0)
{
    fprintf(stderr, "Socket() Error - %s\n", strerror(errno));
    perror("socket");

    exit(1);
}

// Assign sockfd.
con.sockfd = sockfd;

// Set socket option that tells the socket we want to send our own headers.
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0)
{
    fprintf(stderr, "SetSockOpt() Error - %s\n", strerror(errno));
    perror("setsockopt");

    // Close socket.
    close(sockfd);

    exit(1);
}

I am fairly new to C and network programming. Therefore, I apologize if I am missing anything.

If you need any additional information or code, please let me know.

Any help is highly appreciated and thank you for your time!

  • Are your datagrams smaller than 65,507 bytes? – jwdonahue Feb 26 '20 at 00:52
  • And what OS/protocol stack are you running? – jwdonahue Feb 26 '20 at 00:55
  • @jwdonahue I apologize for not including the OS (I thought I did, but I guess not). I'm writing this for Linux (Ubuntu 18.04). And yes, the datagrams are less than 65507 bytes. The MTU size is currently set to 1500 and I can't send any packets higher than that. I'm not sure what you mean by protocol stack. I have edited the post with the code I used to create the socket, though. – Christian Deacon Feb 26 '20 at 01:09
  • When it returns -1, check errno, that should give you a better a clue. – jwdonahue Feb 26 '20 at 01:12
  • This man page covers some of the possible failure modes: https://linux.die.net/man/2/sendto – jwdonahue Feb 26 '20 at 01:15
  • @jwdonahue I wasn't aware `sendto()` set `errno` on failure. Thank you for that. The error I am receiving is 'Message too long'. However, I am only sending around 1400 - 1500 bytes of payload data (it's hitting the MTU limit, though). After researching that error, it sounds like I'll have to handle the fragmentation manually. Though, I'm not entirely sure. – Christian Deacon Feb 26 '20 at 01:21
  • That could be a network or system policy getting in your way. I don't think there's usually any technical reason for UDP not to be able to send larger than the MTU, unless your implementation is just too simple to handle fragments. – jwdonahue Feb 26 '20 at 01:25
  • 1
    Also, almost all system API's that use -1 as a failure return code, set errno with something a little more informative. – jwdonahue Feb 26 '20 at 01:27
  • 1
    Have you looked at socket options `IPPROTO_IP/IP_MTU_DISCOVER` and `IPPROTO_IP/IP_MTU` ? man page ip(7) says (amongst other things) "_While MTU discovery is in progress, initial packets from datagram sockets may be dropped. Applications using UDP should be aware of this and not take it into account for their packet retransmit strategy._" – Chris Hall Feb 26 '20 at 09:43
  • 1
    Hmmm man (7) raw says (in the Bugs section): "When the IP_HDRINCL option is set, datagrams will not be fragmented and are limited to the interface MTU". – C. Gonzalez Feb 26 '20 at 20:46
  • 1
    Check [raw(7)](http://man7.org/linux/man-pages/man7/raw.7.html#NOTES) man page (especially notes and errors sections) and this [response](https://stackoverflow.com/questions/57214403/emsgsize-when-trying-to-send-data-on-raw-ip-packet). – Slava Bacherikov Feb 27 '20 at 16:07
  • I just wanted to thank everyone for the information! It appears fragmentation won't be done automatically when setting the `IP_HDRINCL` socket option unless if MTU Discovery is disabled. I released the code on GitHub [here](https://github.com/gamemann/UDP-Sender/) for anyone curious. It doesn't support fragmentation at the moment, but it's something I'm going to look into when creating my next project which will redirect traffic to a destination using AF_PACKET. – Christian Deacon Feb 29 '20 at 18:29

0 Answers0