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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/973439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:11:50  来源:igfitidea点击:

How to set the don't fragment (DF) flag on a socket?

c++socketsudppacket

提问by WilliamKF

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

我正在尝试设置 DF(不分段标志)以使用 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.

看Richard Steven的书Volume 1 Unix Network Programming;套接字网络 API,我无法找到如何设置它。

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

我怀疑我会用 setsockopt() 来做,但在第 193 页的表格中找不到它。

Please suggest how this is done.

请建议如何做到这一点。

回答by paxdiablo

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

您可以通过setsockopt()调用来实现,方法是使用IP_DONTFRAG选项::

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

Here'sa page explaining this in further detail.

这是一个页面,更详细地解释了这一点。

For Linux, it appears you have to use the IP_MTU_DISCOVERoption with the value IP_PMTUDISC_DO(or IP_PMTUDISC_DONTto turn it off):

对于 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));

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:

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

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

我在我的程序中找不到设置“强制 DF 标志”的任何地方,但tcpdump建议它是。有没有其他方法可以设置?

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_discsysctlfor SOCK_STREAMsockets, and disabled on all others. For non SOCK_STREAMsockets 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).

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

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

在我看来,您可以使用以下命令设置系统范围的默认值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.

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

回答by Asblarf

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().

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

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

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

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_offmember is of type __be16, which can hold 13 + 3 bits.

当您考虑它时,将这两件事分组是有意义的,因为标志与碎片相关。根据RFC-791,描述 IP 报头结构的部分指出 Fragment Offset 是 13 位长,并且有三个 1 位标志。该frag_off成员的类型为__be16,可容纳 13 + 3 位。

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_DFmask.

我们在这里使用专为特定目的设计的IP_DF掩码来精确设置 DF 位。

IP_DFis defined in net/ip.h(kernel headers, of course), whereas struct iphdris defined in linux/ip.h.

IP_DFnet/ip.h(当然是内核头文件)struct iphdr中定义,而在linux/ip.h.

回答by EdwardLewis

I agree with the paxdiablo's answer.

我同意paxdiablo的回答。

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

where valis one of:

其中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.  */
#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_discin kernel source:
  • ip_no_pmtu_disc在内核源代码中:
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;