【问题标题】:Prevent kernel from processing TCP segments bound to a raw socket防止内核处理绑定到原始套接字的 TCP 段
【发布时间】:2015-10-24 02:02:43
【问题描述】:

根据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,我们确实有一个解决方法。但是,如果这里有人有答案并愿意回答,请保持问题的开放性。

【问题讨论】:

  • 我可以创建一个虚拟 tcp-socket 并误导内核将数据包转发到两个套接字(虚拟 tcp-socket 以及我的原始套接字)。虚拟套接字只会吸收数据包而不做任何事情,而我的原始套接字将完成它的设计目的。但这是唯一的方法吗?
  • 您可以使用 AF_PACKET 并且内核不会对您的数据包执行任何操作(一直到 MAC 层)。但我不确定这是否是你想要的。
  • 当我尝试这样做时,“socketFd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);”,套接字创建失败,提示“协议不可用”
  • 您是否以 root 身份运行您的程序?普通用户无法打开原始套接字。
  • 是的,它正在由root用户“root@xxx:”运行

标签: c raw-sockets ubuntu-15.04


【解决方案1】:

我假设原始套接字是在第 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 数据包,但内核也响应这些数据包(在我的情况下 它RSTs链接)。我认为这是因为内核认为没有 任何监听该数据包的端口的人。

你不能阻止内核完成它的工作。从原始套接字手册页:

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

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

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

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

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

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

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

【讨论】:

  • 您好,感谢您提供如此详尽的答案。关于“IPPROTO_IP”的使用,我认为它说“给我来自所有协议的数据包”。关于“IPPROTO_RAW”的使用,我想我没有正确阅读手册页:-) 关于您对 iptables 进行更改的建议,我认为更改将影响整个系统。但我的意图是只为我感兴趣的端口阻止它。所以现在,我要创建另一个 dummy-tcp 套接字,这样内核也可以与我的原始套接字并行完成它的工作。这是我能想到的最好的 :-) 希望没问题
  • @RajKumar 是的,iptables 规则会影响整个系统。您可以通过指定端口来改进它,但我同意它并不优雅。创建一个虚拟 TCP 套接字也是一个好主意 - 我会说比我的建议更好。我将把它添加到我的答案中。
  • 感谢您抽空菲利佩。那么我们应该结束这个问题吗?我想除了我们正在做的解决方法之外,我们可以保持开放,让别人看看并提供答案。可以吗?我将更新问题,以便只查看主要问题 4,因为其他事情更多是我的错误。
  • @RajKumar 我认为我们不应该关闭它。这是一个有用的问题,它有助于建立 stackoverflow 的知识库。无论如何,不​​要删除它。如果您认为我的回答有用,您可以通过点击旁边的复选标记将其标记为已接受(但这不是强制性的)。
  • @RajKumar 我也不会删除其他问题。其他人可能也有类似的问题。但当然,这取决于你 - 只是我的 2 美分 :)
【解决方案2】:

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

关于 AF-PACKET 插座

man packet 中描述了使用 AF_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 套接字相同的事情,但更简单)

链接到guide for capturingman page for sending

【讨论】:

    【解决方案3】:

    以下链接在这里 http://www.tenouk.com/Module43a.html 是从这个问题的答案中检索到的 Simple raw socket server in C/C++ on Linux 它建议您必须是 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);
    
       }
    

    【讨论】:

    • 嗨,我仅以 root 用户身份运行我的应用程序
    • OP 已经表明他尝试过这个。从某处复制和粘贴代码并不是一个好的答案,尤其是当 OP 询问的代码相同时。
    • 很公平。无论如何都要查看链接,它似乎详细说明了如何做低级的事情。
    • 对不起@Jonathan,他当时没有提到他以root身份运行。其次,这是来自链接的引用,其中特别提到需要root。他的问题的其他cmet没有提供任何或链接......将来不会打扰。
    • 问题不在于链接。问题是这并不能回答问题。他问了4个问题,你一个都没回答。我们当然感谢您的努力,但您需要阅读问题并确保在尝试回答之前理解它。这不是唯一的事情,这是一般建议。不要亲自投票(不是我)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-22
    相关资源
    最近更新 更多