【发布时间】:2017-03-27 16:37:45
【问题描述】:
问题:在原始套接字上,recvfrom 可以捕获比sendto 可以发送更多的字节,从而阻止我重新传输大于 MTU 的数据包。
背景:我正在编写一个将捕获和重新传输数据包的应用程序。基本上主机 A 向 X 发送数据,记录它们并将它们转发给 B,所有 Linux 机器。我正在使用原始套接字,因此我可以捕获所有数据,并且它是使用 socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) 创建的。
然后,有代码等待并读取传入的数据包:
const int buffer_size = 2048;
uint8_t* buffer = new uint8_t[buffer_size];
sockaddr_ll addr = {0};
socklen_t addr_len = sizeof(addr);
int received_bytes = recvfrom(_raw_socket, buffer, buffer_size, 0, (struct sockaddr*)&addr, &addr_len);
随后进行数据包处理,循环结束,再次发送数据包:
struct sockaddr_ll addr;
memset(&addr, 0, sizeof(struct sockaddr_ll));
addr.sll_family = htons(AF_PACKET);
addr.sll_protocol = eth_hdr->type;
addr.sll_ifindex = interface().id();
addr.sll_halen = HardwareAddress::byte_size;
memcpy(&(addr.sll_addr), eth_hdr->dest_mac, HardwareAddress::byte_size);
// Try to send packet
if(sendto(raw_socket(), data, length, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0)
问题是我不希望收到大于以太网 MTU(1500 字节)的数据包,而且我不应该收到,因为我使用的是单独处理每个数据包的原始套接字。但有时我确实收到大于 MTU 的数据包。我认为这可能是我的代码中的错误,但 Wireshark 确认如图所示,因此必须在较低级别进行一些重组,例如网络控制器本身。
好吧,好吧,那么我认为没有办法仅对一个应用程序禁用此功能,而且我无法更改主机配置,因此我可能会增加缓冲区大小。但问题是,当我使用大于 MTU 大小(实际上是 1514B,因为 eth 标头)调用 sendto 时,我得到 80: Message too long errno。这就是上面提到的问题——我不能发送我收到的同一个数据包。对此有什么可能的解决方案?我需要多大的缓冲区才能始终捕获整个数据包?
编辑:我刚刚检查了带有ethtool -k interf 的机器并在所有这些机器上都得到了tcp-segmentation-offload: on,所以看起来它真的是网卡重组碎片。但我想知道为什么sendto 的行为不像recvfrom。如果数据包可以自动重组,为什么不分片呢?
附注:应用程序需要发送这些数据包。使用 iptables 等设置转发将不起作用。
【问题讨论】:
标签: c linux sockets networking ethernet