【问题标题】:How to set the don't fragment (DF) flag on a socket?如何在套接字上设置不分段(DF)标志?
【发布时间】:2010-11-01 16:29:07
【问题描述】:

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

看 Richard Steven 的书 Volume 1 Unix Network Programming; Sockets Networking API,我找不到如何设置它。

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

请建议这是如何完成的。

【问题讨论】:

    标签: c++ sockets udp packet


    【解决方案1】:

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

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

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

    对于 Linux,您似乎必须使用带有值 IP_PMTUDISC_DOIP_MTU_DISCOVER 选项(或 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 中定义的路径 MTU 发现。在所有传出数据报上都设置了不分段标志。对于SOCK_STREAM 套接字,系统范围的默认值由ip_no_pmtu_disc sysctl 控制,并在所有其他套接字上禁用。对于非SOCK_STREAM 套接字,用户有责任将数据打包成MTU 大小的块,并在必要时进行重传。如果设置了这个标志(EMSGSIZE),内核将拒绝大于已知路径 MTU 的数据包。

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

        sysctl ip_no_pmtu_disc
    

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

    【讨论】:

    • IPPROTO_IP 在哪个级别?
    • 还有其他方法可以设置吗?我想默认情况下DF是关闭的,对吧?
    • 我的 Centos 4 系统没有定义 IP_DONTFRAG(编译器错误)。此外,我在我的程序中找不到设置它的位置,但 tcpdump() 表明它已设置,并且我收到“消息太长”(90)EMSGSIZE 错误,表明它也已打开,并且数据包太大,表明在许多成功的数据包到具有较小 MTU 的路由后,路由更改了中间传输。
    • BSD Unix98应该支持它,但我不确定Linus是否关心满足标准:-)我在我的Linux系统上也找不到它尽管有其他选择。
    • 仅供参考,正确的 sysctl 检查是:sysctl net.ipv4.ip_no_pmtu_disc。
    【解决方案2】:

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

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

    考虑到这一点,将这两件事归为一组是有意义的,因为标志与碎片相关。根据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 iphdrlinux/ip.h 中定义。

    【讨论】:

      【解决方案3】:

      我同意 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 意味着永远不要分片?
      猜你喜欢
      • 2020-12-24
      • 2011-03-30
      • 2018-02-07
      • 1970-01-01
      • 1970-01-01
      • 2016-10-12
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多