如何在套接字上设置不分段(DF)标志?

15

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

查看Richard Steven的书籍《Unix网络编程》第一卷:套接字网络API,我无法找到如何设置此标志。

我认为可以使用setsockopt()函数来实现,但在第193页的表格中找不到它。

请建议如何完成此操作。

3个回答

28

您可以使用setsockopt()调用,并使用IP_DONTFRAG选项来完成此操作:

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

这里有一篇进一步解释此问题的页面。

对于Linux系统,似乎您需要使用IP_MTU_DISCOVER选项并将其值设置为IP_PMTUDISC_DO(或者IP_PMTUDISC_DONT关闭它):

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

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

至于是否还有另一种方法可以设置DF标志:

我在我的程序中找不到任何地方设置“强制DF标志”,但是tcpdump表明它已经被设置了。还有其他方法可以设置吗?

根据这个优秀的页面here

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

在我看来,您可以使用sysctl设置系统范围内的默认值:

    sysctl ip_no_pmtu_disc

在我的系统上返回"error: "ip_no_pmtu_disc" is an unknown key",但是它可能在您的系统上设置过。除此之外,我不知道其他任何影响设置的东西(除了之前提到的setsockopt())。


3
我的Centos 4系统中未定义IP_DONTFRAG(编译器错误)。此外,我在程序中找不到它被设置的任何地方,但是tcpdump()建议它已经被设置了,并且我收到了一个“消息太长”(90)EMSGSIZE错误,表明它也被打开并且数据包太大,这表明在许多成功传输数据包后,路由发生了变化并且转移到了MTU更小的路由中。 - WilliamKF
1
它应该在BSD和Unix98中得到支持,但我不确定Linus是否那么关心符合标准 :-) 尽管有其他选项,但我也无法在我的Linux系统中找到它。 - paxdiablo
注意:对于Windows(XP及更高版本),该值称为IP_DONTFRAGMENT。请参阅http://www.nil.si/ipcorner/IP_Fragmentation/。 - Suma
愚蠢的问题:IP_DONTFRAG选项在哪里设置?我正在使用Ubuntu,我认为这是一个文件中的参数。我不知道文件在哪里。 - Sablefoste
5
FYI,正确的sysctl检查是:sysctl net.ipv4.ip_no_pmtu_disc。 - jschultz410
显示剩余8条评论

5
如果您在用户空间工作,意图绕过内核网络堆栈并构建自己的数据包和头部,并将它们交给自定义内核模块,那么有一个比setsockopt()更好的选择。
实际上,您可以像任何其他struct iphdr字段一样设置DF标志,该结构定义在linux/ip.h中。 3位IP标志实际上是结构体中的frag_off(片段偏移)成员的一部分。
想一想,将这两件事分组是有道理的,因为标志与分段相关。 根据RFC-791,描述IP头结构的部分说明片段偏移量为13位长,有三个1位标志。 frag_off成员的类型为__be16,可以容纳13 + 3位。
长话短说,这里是一个解决方案:
struct iphdr ip;
ip.frag_off |= ntohs(IP_DF);

我们在这里使用专为此目的设计的IP_DF掩码来精确设置DF位。 IP_DFnet/ip.h(当然是内核头文件)中定义,而struct iphdr则在linux/ip.h中定义。

5

我同意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;

那么 IP_PMTUDISC_DO 的意思是始终不分段吗? - pellucidcoder
只是为了明确“ip_no_pmtu_disc”设置--它不是相同的0-3值!请注意,“0”表示“IP_PMTUDISC_WANT”(值为1)。 - theicfire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接