我假设原始套接字是在第 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 =
套接字(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
数据包,因为它不知道指定端口上的活动 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 身份运行)。