【问题标题】:EMSGSIZE when trying to send data on raw IP packet尝试在原始 IP 数据包上发送数据时的 EMSGSIZE
【发布时间】:2019-12-04 11:11:03
【问题描述】:

我的代码向12.12.12.12 发送了一个原始IP 数据包,但由于EMSGSIZE 而失败。我认为它根据以太网 MTU 限制了我的数据包,但它应该发送数据包

我尝试向127.0.0.1 发送数据包,效果很好,但是当我向非本地 IP 发送数据包时出现错误。

#include <assert.h>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void) {
    int fd;
    assert((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(1818);
    assert(inet_aton("12.12.12.12", &addr.sin_addr) != -1);
    assert(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != -1);

    char buffer[2000];
    memset(buffer, '\0', sizeof(buffer));
    assert(send(fd, buffer, sizeof(buffer), 0) == sizeof(buffer));

    assert(close(fd) != -1);
}

我希望代码能够正常工作,因为我发送的数据包小于 IP MTU。使用strace 解决代码问题:

socket(AF_INET, SOCK_RAW, IPPROTO_RAW)  = 3
connect(3, {sa_family=AF_INET, sin_port=htons(1818), sin_addr=inet_addr("12.12.12.12")}, 16) = 0
sendto(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 2000, 0, NULL, 0) = -1 EMSGSIZE (Message too long)
a.out: compile.c:22: main: Assertion `send(fd, buffer, sizeof(buffer), 0) == sizeof(buffer)' failed.
--- SIGABRT {si_signo=SIGABRT, si_code=SI_TKILL, si_pid=8814, si_uid=0} ---
+++ killed by SIGABRT (core dumped) +++
Aborted

【问题讨论】:

  • assert 中的所有内容都在发布版本中被删除。整个assert 和表达式有效地变成了空格。你应该做类似ssize_t result = send(fd, ...); assert(result == sizeof(buffer)); 的事情。在发布版本中,send 将继续存在。另见Is assert(false) ignored in release mode?
  • 如果你执行ssize_t result = send(fd, ...);那么你可以检查result。根据send(2) man page“成功时,这些调用返回发送的字符数。出错时,返回-1,并正确设置errno。”您应该可以检查@987654336 @ 对于类似EMSGSIZE 的东西,在手册页中有记录:“如果消息太长而无法原子地通过底层协议,则返回错误 EMSGSIZE,并且不会传输消息。”我>
  • 我使用gcc test.c 编译代码,而NDEBUG 没有定义,所以断言运行良好(正如我们在strace 消息中看到的那样)。
  • 使用 gcc 在 linux 上编译时,没有编译错误。执行链接步骤时,没有错误。但是,当尝试运行发布的代码时,第一次调用 assert() 会导致代码中止。强烈建议删除/替换对assert() 的调用,例如:if( (fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) &lt; 0) { perror( "socket failed" ); }
  • 使用对perror() 的建议调用会导致:socket failed: Operation not permitted。这提示socket()的参数组合无效

标签: c linux sockets networking raw-sockets


【解决方案1】:

我认为它根据以太网 MTU 限制了我的数据包,但它应该发送数据包

由于默认情况下 PMTU 处于开启状态,它只会发送适合 MTU 的数据包。你的包没有。 来自raw(7)

默认情况下,原始套接字执行路径 MTU(最大传输单元) 发现。这意味着内核将跟踪 MTU 到 特定的目标 IP 地址和在原始数据包时返回 EMSGSIZE 写超过它。发生这种情况时,应用程序应该减少 数据包大小。
路径 MTU 发现也可以关闭,使用 IP_MTU_DISCOVER 套接字选项或 /proc/sys/net/ipv4/ip_no_pmtu_disc 文件,详见 ip(7)。什么时候 关闭后,原始套接字将对超出的传出数据包进行分段 接口 MTU。但是,不建议禁用它 性能和可靠性方面的原因。

【讨论】:

    猜你喜欢
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    相关资源
    最近更新 更多