8

Can UDP packet be fragmented to several smaller ones if it exceeds MTU? It seems that MTU fragmentation is about IP layer so I think it can.

If so, what is the recommended max. packet size to send over UDP to avoid fragmentation and why?

dbush
  • 205,898
  • 23
  • 218
  • 273
FrozenHeart
  • 19,844
  • 33
  • 126
  • 242

2 Answers2

10

Any IP datagram can be fragmented if it is larger than the MTU. Whether it contains UDP, TCP, ICMP, etc. does not matter.

Most Ethernet networks support a 1500 byte MTU. With the IPv4 header being 20 bytes and the UDP header being 8 bytes, the payload of a UDP packet should be no larger than 1500 - 20 - 8 = 1472 bytes to avoid fragmentation.

This is assuming no IP options exist in the packet. If so, the payload will need to be smaller than that to account for it.

This applies to IPv4 only. IPv6 does not support fragmentation.

You can see also this question regarding MTU for UDP.

ctrl-alt-delor
  • 7,506
  • 5
  • 40
  • 52
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Just making sure: does fragmenting the packets and sending via UDP assure packet order? Am I responsible for packet ordering on the receiving side? – DifferentPulses Sep 02 '21 at 16:37
  • 1
    @DifferentPulses When IP fragmentation occurs, the IP stack won't pass any messages to the user until all fragments of the IP datagram are received. With regard to UDP, it makes no guarantees regarding packet order, so it's up to you to reorder the packets on the receiving side. – dbush Sep 02 '21 at 17:53
-2

UDP packet fragmentation is typically handled by the underlying network stack and is transparent to the application layer. However, if you want to manually fragment UDP packets into arbitrary sizes, the following example would be helpful.

#define UDP_FRAG_1024 1024

static int udp_raw_socket = -1;
static int udp_ip_iden = 1234;

int udp_frag1024_sendto(int s, caddr_t buf, int buf_len, int flags, struct sockaddr *to, int to_len)
{
    /* You must be in the sudoers files. */

    struct sockaddr_in sin;
    struct sockaddr_in *ptr_din = (struct sockaddr_in *)to;
    int sin_len = sizeof(sin);
    unsigned char packet[1500];
    short packet_len;
    short ip_iden;
    int sent1;
    int sent2;

    if (udp_raw_socket == -1) {
        int opt_val = 1;
        int *ptr_opt_val = &opt_val;

        udp_raw_socket = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);

        (void)setsockopt(udp_raw_socket,IPPROTO_IP,IP_HDRINCL,(char *)ptr_opt_val,sizeof(opt_val));
    }

    if (buf_len > 2*UDP_FRAG_1024) {
        printf("buf_len %d not supported.\n",buf_len);
        return -1;
    }
    else if (buf_len <= UDP_FRAG_1024) {
        return sendto( s, buf, buf_len, flags, to, to_len );
    }
    else {
        ip_iden = udp_ip_iden++;

        (void)getsockname(s,(struct sockaddr *)&sin,&sin_len);

        /* 1st framentation - IP header */

        packet_len = 20 + 8 + UDP_FRAG_1024;

        packet[0] = 0x45; /* ver and header length */
        packet[1] = 0x00; /* tos */
        *(short *)&packet[2] = htons(packet_len);
        *(short *)&packet[4] = htons(ip_iden);
        packet[6] = 0x20; packet[7] = 0x00; /* flag */
        packet[8] = 0x40; /* ttl */
        packet[9] = 0x11; /* udp */
        packet[10] = 0x00; packet[11] = 0x00;/* checksum */
        memcpy( &packet[12], &sin.sin_addr.s_addr, 4 );
        memcpy( &packet[16], &ptr_din->sin_addr.s_addr, 4 );

        /* 1st framentation - UDP header */

        memcpy( &packet[20], &sin.sin_port, 2 );
        memcpy( &packet[22], &ptr_din->sin_port, 2 );
        *(short *)&packet[24] = htons(8+buf_len);
        packet[26] = 0x00; packet[27] = 0x00; /* checksum */

        /* 1st framentation - payload */

        memcpy( &packet[28], buf, UDP_FRAG_1024 );

        sent1 = sendto(udp_raw_socket,packet,packet_len,0,to,sizeof(struct sockaddr_in));

        /* 2nd framentation */

        packet_len = 20 + buf_len - UDP_FRAG_1024;

        *(short *)&packet[2] = htons(packet_len);
        packet[6] = 0x00; packet[7] = (8+UDP_FRAG_1024)/8; /* flag:0x81*8=129*8=1032=8+1024 */
        packet[10] = 0x00; packet[11] = 0x00; /* checksum */

        memcpy( &packet[20], buf+UDP_FRAG_1024, buf_len-UDP_FRAG_1024 );

        sent2 = sendto(udp_raw_socket,packet,packet_len,0,to,sizeof(struct sockaddr_in));
    }

    return ((sent1 - 20 - 8) + (sent2 - 20));
}

void udp_frag1024_sendto_test()
{
    int s;
    struct sockaddr_in my_addr;
    struct sockaddr_in to_addr;
    char buffer[4096];
    int sent;

    s = socket(AF_INET, SOCK_DGRAM, 0);

    my_addr.sin_family      = AF_INET;
    my_addr.sin_addr.s_addr = inet_addr("192.168.0.229");
    my_addr.sin_port        = htons(12345);

    bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr));

    to_addr.sin_family      = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr("192.168.0.19");
    to_addr.sin_port        = htons(12345);

    memset(buffer,'A',sizeof(buffer));

    sent = udp_frag1024_sendto(s,buffer,2047,0,(struct sockaddr *)&to_addr,sizeof(struct sockaddr_in));

    close(s);

    printf("sent %d bytes\n",sent);
}
JaeMann Yeh
  • 358
  • 2
  • 9