Как отправлять пакеты по значению MTU

Я пытаюсь реализовать свой собственный протокол через UDP.

Как предлагают многие руководства в Интернете, лучше избегать фрагментации IP-адресов, отправляя пакеты с размерами, меньшими, чем MTU.

Интересно, как лучше всего получить оптимальный размер сообщения? Должен ли я каким-то образом получить значение MTU (например, вот так) или просто установить его как что-то вроде 1300 или 1400 и надеяться, что оно не будет меньше или изменится со временем?

Я слышал, что есть некоторые проблемы с получением значения MTU ( https://en.wikipedia.org/wiki/Path_MTU_Discovery) и, насколько я знаю, это сильно зависит от текущего маршрута и других факторов, которые могут меняться со временем.

2 ответа

Решение

Рекомендуемый размер для IPv4 UDP составляет 576 октетов. Предполагается, что каждый интернет-маршрутизатор гарантирует MTU IPv4, по крайней мере, такого размера, и, поскольку UDP - это протокол без установления соединения, безотказный, с максимальными усилиями, без гарантированной доставки, вы будете рисковать меньшим объемом данных с каждым пакетом, который может будут потеряны, и будут потеряны пакеты.

IPv6 имеет минимальное требование MTU 1280 октетов и не имеет фрагментации на пути.

Для получения MTU в вашем интерфейсе, а не для обнаружения Path MTU, у вас есть struct ifreq. Одним из его полей является ifr_mtu, и это поле предоставит вам MTU. Вы читаете это поле с помощью ioctl, SIOCGIFMTU для правильного интерфейса. ( 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;
   };
};

Пример:

#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;
}

Сегодня в целом вы можете доверять MTU 1500 в Интернете, если вы не используете специальные технологии подключения, такие как Bluetooth или Zigbee. Вы можете использовать Path MTU Discovery и реализовать свой протокол на основе UDP с ACK, чтобы проверить, что другая сторона получила сообщение. В настоящее время реализация ACK и функций протокола, ориентированного на установление соединения, отличается от использования TCP. Если вы можете делать все с UDP, он легче, чем TCP.

Изменить: Для использования Path MTU Discovery вы также можете использовать getsockopt с опцией 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.

Смотрите больше здесь: http://man7.org/linux/man-pages/man7/ip.7.html

Другие вопросы по тегам