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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With