1

I'm trying to implement my own protocol over UDP.

As suggested by many manuals in Internet, it's better to avoid IP fragmentation by sending packets with their sizes less than MTU.

I wonder what is the best way to get the optimal size of the message? Should I somehow get MTU value (like this, for example) or should I just set it to smth like 1300 or 1400 and hope that it won't be less or change over time?

I heard that there are some problems with getting MTU value (https://en.wikipedia.org/wiki/Path_MTU_Discovery) and as far as I know it highly depends on the current route and other factors that can change over time.

Community
  • 1
  • 1
FrozenHeart
  • 19,844
  • 33
  • 126
  • 242

2 Answers2

2

The recommended size for IPv4 UDP is 576 octets. Each Internet router is supposed to guarantee an IPv4 MTU of at least that size, and since UDP is a connectionless, fire-and-forget, best-effort, no-guaranteed-delivery protocol, you will risk less data with each packet that may be lost, and there will be lost packets.

IPv6 has a minimum MTU requirement of 1280 octets, and no fragmentation in the path.

Ron Maupin
  • 6,180
  • 4
  • 29
  • 36
  • And why don't you recommend to get the MTU during run-time? – FrozenHeart Aug 07 '16 at 18:19
  • Because you don't have a guarantee of the path. Most likely that path will not change, but it may, and if the path changes, the MTU may change. If you are worried about it, set the DF bit and look for ICMP error messages about it. The recommended UDP size is not _my_ recommendation. The best use of UDP is with small datagrams so that little is lost when packets get lost. That's why voice and video using UDP have very small datagrams. – Ron Maupin Aug 07 '16 at 18:24
  • Also, congestion, meaning packet drops, _will_ continuously change, and it will probably be worse at certain times of the day or night. If you have a high tolerance for lost data, then use a larger payload, otherwise keep it low. – Ron Maupin Aug 07 '16 at 18:50
2

For obtaining the MTU in your interface, not for Path MTU discovery, you have struct ifreq. One of its fields is ifr_mtu and this field will provide you the MTU. You read this field with ioctl, SIOCGIFMTU for the proper interface. (http://man7.org/linux/man-pages/man7/netdevice.7.html)

struct ifreq {
   char ifr_name[IFNAMSIZ]; /* Interface name */
   union {
       struct sockaddr ifr_addr;
       struct sockaddr ifr_dstaddr;
       struct sockaddr ifr_broadaddr;
       struct sockaddr ifr_netmask;
       struct sockaddr ifr_hwaddr;
       short           ifr_flags;
       int             ifr_ifindex;
       int             ifr_metric;
       int             ifr_mtu;
       struct ifmap    ifr_map;
       char            ifr_slave[IFNAMSIZ];
       char            ifr_newname[IFNAMSIZ];
       char           *ifr_data;
   };
};

Example:

#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>


int main(void)
{
    int sock;
    char *name = "enp0s3";
    struct ifreq ifr;

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("Creating socket: %d\n", errno); 
        exit(-1);
    }

    ifr.ifr_addr.sa_family = AF_INET;
    strcpy(ifr.ifr_name, name);
    if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
        printf("Error ioctl: %d\n", errno);
        exit(-2);
    }

    printf("MTU is %d.\n", ifr.ifr_mtu);

    close(sock);

    return 0;
}

Today, in general you can trust an MTU of 1,500 in the Internet unless you use specific connection technologies like Bluetooth or Zigbee. You can use Path MTU Discovery and implement your UDP-based protocol with an ACK to check the other side has received the message. Now, implementing an ACK and features of connection-oriented protocol is not the same as using TCP. If you can do everything with UDP it is lighter than TCP.

Edit: For using Path MTU Discovery, you can also use getsockopt with option IP_PMTUDISC_DO:

IP_MTU_DISCOVER (since Linux 2.2)
              Set or receive the Path MTU Discovery setting for a socket.
              When enabled, Linux will perform Path MTU Discovery as defined
              in RFC 1191 on SOCK_STREAM sockets.  For non-SOCK_STREAM
              sockets, IP_PMTUDISC_DO forces the don't-fragment flag to be
              set on all outgoing packets.  It is the user's responsibility
              to packetize the data in MTU-sized chunks and to do the
              retransmits if necessary.  The kernel will reject (with
              EMSGSIZE) datagrams that are bigger than the known path MTU.
              IP_PMTUDISC_WANT will fragment a datagram if needed according
              to the path MTU, or will set the don't-fragment flag
              otherwise.

See more here: http://man7.org/linux/man-pages/man7/ip.7.html

rodolk
  • 5,606
  • 3
  • 28
  • 34
  • "For obtaining the MTU in your interface, not for Path MTU discovery" -- what's the difference? – FrozenHeart Aug 07 '16 at 18:55
  • @FrozenHeart, MTU of your interface refers to the L2 technology of the immediate LAN where your interface is connected. Path MTU is all L2 technologies in the middle between this interface and the destination interface (in the destination node) identified by the destination IP address. – rodolk Aug 07 '16 at 19:20
  • What's the point of knowing MTU of my own interface, not the whole network path? – FrozenHeart Aug 07 '16 at 19:26
  • @FrozenHeart, actually you want to know the whole network path, your interface is the first limit. If you were communicating through different L2 technologies, like WiFi, Zigbee, Bluetooth, in a PAN or LAN then the interface MTU is useful. – rodolk Aug 08 '16 at 14:28