6

根据http://linux.die.net/man/7/rawraw_socket = socket(AF_INET, SOCK_RAW, int protocol);是创建原始套接字的方法。

  1. 我假设原始套接字是在第 3 层创建的,因此协议不应该是IPPROTO_TCP/IPPROTO_UDP但它应该是IPPROTO_IP. 这种理解正确吗?

  2. 但是当我使用协议创建原始套接字时IPPROTO_IP*socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);),套接字创建失败并出现错误协议不支持

  3. 当我使用IPPROTO_RAW( *socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);) 协议创建原始套接字时,我的应用程序没有收到任何数据包

  4. 当我使用IPPROTO_TCP( socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);) 协议创建原始套接字时,我的应用程序会接收 TCP 数据包,但内核也会响应这些数据包(在我的情况下,它会 RST 链接)。我认为这是因为内核认为没有人在监听该数据包的目标端口。

我的目的只是用虚假的 IP 和 TCP 标头对发送到我的应用程序的消息发送响应。由于上述尝试都不适合我,我应该如何创建原始套接字并使内核 TCP 层仅针对该连接保持安静?

编辑:请跳过问题 1-3。Filipe 已经回答了他们。对于问题 4,我们确实有一个解决方法。但是,如果有人在这里有答案并想回答,请保持问题的开放性。

4

3 回答 3

7

我假设原始套接字是在第 3 层创建的,因此协议不应该是 IPPROTO_TCP / IPPROTO_UDP,而应该是 IPPROTO_IP。这种理解正确吗?

不。你说得对,原始套接字基本上是第 3 层数据包,但协议不应该是IPPROTO_IP. 原始套接字情况下的协议参数指示您有兴趣在该套接字上接收哪种类型的数据包。请记住,协议本质上执行传输级解复用,因此您需要指定原始套接字感兴趣的协议类型。这在以下内容中明确man 7 raw

与为原始套接字指定的协议号匹配的所有数据包或错误都会传递到此套接字。有关允许的协议列表,请参阅 RFC 1700 分配的编号和 getprotobyname(3)。

由于您有兴趣接收 TCP 连接的 IP 数据包,因此您应该使用IPPROTO_TCP.

但是,当我使用协议作为 IPPROTO_IP (*socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);) 创建原始套接字时,套接字创建失败并出现错误协议不支持。

是的,这是可以预料的:IP 协议不是第 4 层协议。正如我所说,协议字段用于传输层解复用,因此使用IPPROTO_IP.

当我使用 IPPROTO_RAW (*socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);) 协议创建原始套接字时,我的应用程序没有收到任何数据包

那是因为IPPROTO_RAW意味着您对发送所有类型的协议数据包(TCP、UDP 或任何其他协议)感兴趣。但是IPPROTO_RAW你不能做相反的事情:IPPROTO_RAW这意味着你可以在这个原始套接字中接收任何协议,这是不受支持的。这在以下内容中也很清楚man 7 raw

IPPROTO_RAW 协议意味着启用 IP_HDRINCL 并且能够发送在传递的标头中指定的任何 IP 协议。使用原始套接字无法通过 IPPROTO_RAW 接收所有 IP 协议。

换句话说,IPPROTO_RAW使您能够发送与任何协议匹配的数据包,但代价是阻止您得到回复。您可以创建与协议绑定的其他特定原始套接字以获取回复作为解决方法,但这会使设计复杂化,因为您必须管理原始套接字池,而且这绝对不是您想要做的。

当我使用协议作为 IPPROTO_TCP (socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);) 创建原始套接字时,我的应用程序会接收 TCP 数据包,但内核也会响应这些数据包(在我的情况下,它会 RST 链接)。我认为这是因为内核认为没有人在监听该数据包的目标端口。

你不能阻止内核完成它的工作。从原始套接字联机帮助页:

当一个数据包被接收到时,它在被传递给其他协议处理程序(例如,内核协议模块)之前被传递到任何已经绑定到它的协议的原始套接字。

因此,内核发送RST数据包是对的,因为它不知道指定端口上的活动 TCP 套接字或连接。正如我所说,你不能阻止内核完成它的工作,但是一个相对快速(也许是丑陋)的黑客是使用 iptables 丢弃 RST 数据包:

iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP

是的,不是很优雅,但我认为我们在这里无能为力。

正如评论中所建议的,您还可以创建一个虚拟 TCP 套接字,该套接字绑定到您刚刚接收和丢弃消息的相同端口和地址。这样内核就不会发送RST回复,你也不需要弄乱 iptables。

还要记住,由于您需要IPPROTO_TCP为原始套接字指定,您应该IP_HDRINCL在套接字上设置,setsockopts(2)以便您可以构建自定义 IP 标头。

最后,确保运行它的进程具有有效的用户 ID 0 或CAP_NET_RAW能力(实际上:以 root 身份运行)。

于 2015-08-01T13:54:00.297 回答
0

[这是对 Jonathon Reinhart 评论的评论,但我没有足够的声誉]

关于 AF-PACKET 插座

使用 AF_PACKET 创建原始套接字在man packet. “protocol”变量的值应该是网络字节顺序中的 ethertype 值。这些值在<linux/if_ether.h>(按主机字节顺序)中定义。所以要打开一个原始套接字,你要做

socketFd = socket ( AF_PACKET , SOCK_RAW , htons ( ETH_P_IP ) );

如果您需要 AF_PACKET 套接字,您还可以使用 libpcap/tcpdump。它将允许您捕获以太网帧并发送原始以太网帧。对于捕获,您可以对想要的帧设置过滤器(例如,TCP 端口 X)。(基本上使用 libpcap,您可以执行与使用 AF_PACKET 套接字相同的操作,但更简单)

指向捕获指南发送手册页的链接

于 2015-08-01T17:07:11.983 回答
-2

以下链接 http://www.tenouk.com/Module43a.html 是从这个问题的答案中检索到的 Linux 上 C/C++ 中的简单原始套接字服务器 它建议您必须是 root 或以 setuid0 身份运行才能使用原始套接字

/* Must be root or SUID 0 to open RAW socket */
...
 /* Create RAW socket */

   if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)

   {

    perror("socket() error");

    /* If something wrong, just exit */

    exit(1);

   }
于 2015-08-01T13:48:06.543 回答