Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send packets according to the MTU value

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.

like image 508
FrozenHeart Avatar asked Dec 05 '25 06:12

FrozenHeart


1 Answers

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 work with an MTU of 1,500 in the Internet unless you use specific connection technologies like Bluetooth or Zigbee. With cellular technologies MTU will be lower, it may be around 1400-1430, depending on the carrier. 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

like image 75
rodolk Avatar answered Dec 08 '25 02:12

rodolk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!