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

发布于 2024-07-23 01:54:17 字数 216 浏览 7 评论 0原文

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

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

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

请建议如何完成此操作。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酒几许 2024-07-30 01:54:21

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

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

当您考虑它时,将这两件事分组是有意义的,因为标志与碎片相关。 根据RFC-791,描述IP头结构的部分指出Fragment Offset是13 位长,有 3 个 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

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

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

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

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_DF mask.

IP_DF is defined in net/ip.h (kernel headers, of course), whereas struct iphdr is defined in linux/ip.h.

怀念你的温柔 2024-07-30 01:54:21

我同意paxdiablo的回答。

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

其中 val 是以下之一:

#define IP_PMTUDISC_DONT 0 /* 从不发送 DF 帧。   */ 
  #define IP_PMTUDISC_WANT 1 /* 使用每个路由提示。   */ 
  #define IP_PMTUDISC_DO 2 /* 始终为 DF。   */ 
  #define IP_PMTUDISC_PROBE 3 /* 忽略 dst pmtu。   */ 
  
  • 内核源代码中的ip_no_pmtu_disc
if (ipv4_config.no_pmtu_disc) 
      inet->pmtudisc = IP_PMTUDISC_DONT; 
  别的 
      inet->pmtudisc = IP_PMTUDISC_WANT; 
  

I agree with the paxdiablo's answer.

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

where val is one of:

#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 in kernel source:
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;
从此见与不见 2024-07-30 01:54:18

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

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

这里页面对此进行了更详细的解释。

对于 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 表明确实如此。 还有其他方法可以设置吗?

从这个优秀的页面此处

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() 之外)会影响设置。

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

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

Here's a page explaining this in further detail.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文