views:

2165

answers:

1

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.

+7  A: 

You do do it with the setsockopt() call, by using the IP_DONTFRAG option. This is from memory:

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

Here's a page explaining this in further details. A cursory glance seems to indicate my memory is intact, despite the damage inflicted by years of being under the alfluence of incohol :-)

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
Which level is that under, IPPROTO_IP?
WilliamKF
Is there any other way this could get set? I presume that by default DF is off, right?
WilliamKF
I think it's off by default but you can use a getsockopt (surprisingly enough :-) to get it's current value.
paxdiablo
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
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
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
Note that fragmentation is triggered when the MTU size is crossed. At this time if DF is set, you typically get a EMSGSIZE.
nik
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
@WilliamKF, see my update for the Linux option.
paxdiablo
On Centos 4 doing 'sysctl ip_no_pmtu_disc' I get: error: 'ip_no_pmtu_disc' is an unknown key
WilliamKF
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