我假设原始套接字是在第 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 身份运行)。