如何在套接字上设置do not fragment(DF)标志?

时间:2009-06-10 02:30:56

标签: c++ sockets udp packet

我正在尝试使用UDP设置DF(不分段标志)来发送数据包。

查看Richard Steven的书第1卷Unix网络编程;套接字网络API,我无法找到如何设置它。

我怀疑我会使用setsockopt()执行此操作,但无法在第193页的表中找到它。

请建议如何完成。

3 个答案:

答案 0 :(得分:19)

您可以使用setsockopt()选项::

通过IP_DONTFRAG调用来执行此操作
int val = 1;
setsockopt(sd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));

Here's页面更详细地解释了这一点。

对于Linux,您似乎必须使用值为IP_MTU_DISCOVER的{​​{1}}选项(或IP_PMTUDISC_DO将其关闭):

IP_PMTUDISC_DONT

我没有对此进行测试,只是查看了头文件和一些网页搜索,因此您需要对其进行测试。

关于是否可以设置DF标志的另一种方式:

  

我发现在我的程序中没有设置“强制DF标志”,但int val = IP_PMTUDISC_DO; setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); 表明它是。有没有其他方法可以设置?

从这个优秀的页面here

  

tcpdump设置或接收套接字的Path MTU Discovery设置。启用后,Linux将在此套接字上执行RFC 1191中定义的路径MTU发现。在所有传出数据报上设置了不分段标志。系统范围的默认值由IP_MTU_DISCOVER: ip_no_pmtu_disc用于sysctl套接字控制,并在所有其他套接字上禁用。对于非SOCK_STREAM套接字,用户有责任将数据打包为MTU大小的块,并在必要时进行重新传输。如果设置了此标志(SOCK_STREAM),内核将拒绝大于已知路径MTU的数据包。

我认为您可以使用EMSGSIZE设置系统范围的默认值:

sysctl

在我的系统上返回sysctl ip_no_pmtu_disc ,但可能会在您的系统上设置。除此之外,我不知道可能影响设置的任何其他内容(除前面提到的"error: "ip_no_pmtu_disc" is an unknown key"之外)。

答案 1 :(得分:3)

如果您在Userland工作,意图绕过内核网络堆栈,从而构建自己的数据包和标头并将它们交给自定义内核模块,那么有一个比setsockopt()更好的选择。

您实际上可以设置DF标记,就像struct iphdr中定义的linux/ip.h的任何其他字段一样。 3位IP标志实际上是frag_off的一部分  (Fragment Offset)结构的成员。

当你考虑它时,将这两件事分组是有道理的,因为旗帜与碎片有关。根据{{​​3}},描述IP头结构的部分指出Fragment Offset是13位长并且有三个1位标志。 frag_off成员的类型为__be16,可以容纳13 + 3位。

长话短说,这是一个解决方案:

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

我们在这里使用专门针对特定目的IP_DF掩码设置DF位。

IP_DFnet/ip.h(内核标题,当然)中定义,而struct iphdrlinux/ip.h中定义。

答案 2 :(得分:2)

我同意paxdiablo的回答。

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

val之一是:

#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
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;