15

I am trying to set the DF (don't fragment flag) for sending packets using UDP.

Looking at the Richard Steven's book Volume 1 Unix Network Programming; The Sockets Networking API, I am unable to find how to set this.

I suspect that I would do it with setsockopt() but can't find it in the table on page 193.

Please suggest how this is done.

WilliamKF
  • 41,123
  • 68
  • 193
  • 295

3 Answers3

28

You do it with the setsockopt() call, by using the IP_DONTFRAG option:

int val = 1;
setsockopt(sd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));

Here's a page explaining this in further detail.

For Linux, it appears you have to use the IP_MTU_DISCOVER option with the value IP_PMTUDISC_DO (or IP_PMTUDISC_DONT to turn it off):

int val = IP_PMTUDISC_DO;
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));

I haven't tested this, just looked in the header files and a bit of a web search so you'll need to test it.

As to whether there's another way the DF flag could be set:

I find nowhere in my program where the "force DF flag" is set, yet tcpdump suggests it is. Is there any other way this could get set?

From this excellent page here:

IP_MTU_DISCOVER: Sets or receives the Path MTU Discovery setting for a socket. When enabled, Linux will perform Path MTU Discovery as defined in RFC 1191 on this socket. The don't fragment flag is set on all outgoing datagrams. The system-wide default is controlled by the ip_no_pmtu_disc sysctl for SOCK_STREAM sockets, and disabled on all others. For non SOCK_STREAM sockets 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 packets that are bigger than the known path MTU if this flag is set (with EMSGSIZE).

This looks to me like you can set the system-wide default using sysctl:

    sysctl ip_no_pmtu_disc

returns "error: "ip_no_pmtu_disc" is an unknown key" on my system but it may be set on yours. Other than that, I'm not aware of anything else (other than setsockopt() as previously mentioned) that can affect the setting.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Which level is that under, IPPROTO_IP? – WilliamKF Jun 10 '09 at 02:37
  • Is there any other way this could get set? I presume that by default DF is off, right? – WilliamKF Jun 10 '09 at 02:38
  • I think it's off by default but you can use a getsockopt (surprisingly enough :-) to get it's current value. – paxdiablo Jun 10 '09 at 02:45
  • IP_DONTFRAG is not supported for multicast and i think its not well supported for UDP sockets outside the BSD implementations. If you are not already getting a EOPNOTSUPP error, you can check with a getsockopt after the setsockopt to confirm its done. – nik Jun 10 '09 at 02:55
  • 3
    The IP_DONTFRAG is not defined (compiler error) for my Centos 4 system. Further, I find nowhere in my program where it is set, yet tcpdump() suggests it is set, and I got a 'message too long' (90) EMSGSIZE error suggesting that it is on too and a packet was too large suggesting that the routing changed mid transfer after many successful packets to a route with a smaller MTU. – WilliamKF Jun 10 '09 at 02:59
  • The EMSGSIZE in this condition implies you are trying to send a packet larger than your MTU with the don't fragment bit set. Check your packet size is within MTU. – nik Jun 10 '09 at 03:01
  • Note that fragmentation is triggered when the MTU size is crossed. At this time if DF is set, you typically get a EMSGSIZE. – nik Jun 10 '09 at 03:02
  • 1
    It should be supported in BSD *and* Unix98, but I'm not sure if Linus was that concerned with meeting standards :-) I also cannot find it on my Linux system despite having the other options. – paxdiablo Jun 10 '09 at 03:09
  • On Centos 4 doing 'sysctl ip_no_pmtu_disc' I get: error: 'ip_no_pmtu_disc' is an unknown key – WilliamKF Jun 13 '09 at 00:40
  • Then I don't know why it's being set by default. My suggestion is to unset it using the setsockopt() shown in the answer before using the socket. – paxdiablo Jun 13 '09 at 01:48
  • Note: for Windows (XP and later) the value is called IP_DONTFRAGMENT. See http://www.nil.si/ipcorner/IP_Fragmentation/ – Suma Nov 21 '11 at 13:44
  • Silly question; Where do you set the `IP_DONTFRAG` option? I am using Ubuntu, and I assume this is a parameter in a file. I don't know where the file is. – Sablefoste Feb 27 '15 at 14:17
  • 5
    FYI, the correct sysctl check is: sysctl net.ipv4.ip_no_pmtu_disc. – jschultz410 Nov 16 '17 at 16:48
5

I agree with the paxdiablo's answer.

  • setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))

where val is one of:

#define IP_PMTUDISC_DONT   0    /* Never send DF frames.  */
#define IP_PMTUDISC_WANT   1    /* Use per route hints.  */
#define IP_PMTUDISC_DO     2    /* Always DF.  */
#define IP_PMTUDISC_PROBE  3    /* Ignore dst pmtu.  */
  • ip_no_pmtu_disc in kernel source:
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
EdwardLewis
  • 51
  • 1
  • 1
5

If you are working in Userland with the intention to bypass the Kernel network stack and thus building your own packets and headers and hand them to a custom Kernel module, there is a better option than setsockopt().

You can actually set the DF flag just like any other field of struct iphdr defined in linux/ip.h. The 3-bit IP flags are in fact part of the frag_off (Fragment Offset) member of the structure.

When you think about it, it makes sense to group those two things as the flags are fragmentation related. According to the RFC-791, the section describing the IP header structure states that Fragment Offset is 13-bit long and there are three 1-bit flags. The frag_off member is of type __be16, which can hold 13 + 3 bits.

Long story short, here's a solution:

struct iphdr ip;
ip.frag_off |= ntohs(IP_DF);

We are here exactly setting the DF bit using the designed-for-that-particular-purpose IP_DF mask.

IP_DF is defined in net/ip.h (kernel headers, of course), whereas struct iphdr is defined in linux/ip.h.

Asblarf
  • 483
  • 1
  • 4
  • 14