【问题标题】:Using socket AF_PACKET / SOCK_RAW but tell kernel to not send RST使用套接字 AF_PACKET / SOCK_RAW 但告诉内核不要发送 RST
【发布时间】:2018-02-20 18:14:24
【问题描述】:

我的问题已经大致讨论过here

tl;dr 的解决方案是:

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

您可以修改它以仅阻止您正在监听的端口。
但正如上面问题中提到的,以及here,这些都不是优雅的解决方案。

现在,我并不真正关心事物的优雅。
但我确实关心学习。所以我深入研究了 Linux 源代码(目前主要对基于 Linux 的机器感兴趣) 并整理了我认为的 socket.bind 方法,以便找到与指导内核相关的任何内容“我们”正在积极监控 TCP 端口。

我假设套接字库通知内核一些如何将特定端口绑定到应用程序,这样内核就不会自动以 RST 数据包响应客户端连接,提示 “连接被拒绝”

但是,我在源代码中找不到这样的代码。
packet 手册页也没有告诉我如何通知内核忽略/接受来自特定端口的数据包。

我有一个漂亮的basic socket 设置为以混杂模式收听(这是它自己的故事)

但是,我的问题是,一旦客户端连接,在任何给定端口上 - 内核按预期方式发送传入的 Ethernet+IP+TCP 数据帧 - 但它也会立即发送带有“反向”源的响应和目标端口和RST 标志集。它应该,但不是在我告诉它的特定端口上。问题是,我如何告诉内核我正在监视一个特定的端口?

一种选择是(正如在其他一些论坛和其他各种 SO 线程中所讨论的) - 在该端口上创建一个虚拟套接字。

s = socket()
s.bind(('', <port>))

然而,这会导致一堆其他问题(其中一个是它会有一个缓冲区,很快就会被填满),最重要的是,仍然没有教我如何使用这些魔法发生。如果没有其他方法,上述两个解决方案是最后的手段,但我觉得我比以往任何时候都更接近但也比以往任何时候都更难以找到解决这个问题的适当解决方案。

解决方案或提示也可以在 C 中,和/或内核模块中,只是为了向内核指示所需信息。我知道这一切都在内核中下降,并且在下面的 cmets 和一个非常感谢的解决方案想法之后,我现在知道这个东西没有用户空间功能。我可能/很容易将它移植到 cPython 模块,或者使用内核模块/扩展轻松地将其转换为 Python 代码。但是我真的不知道设置这些东西的内核函数在哪里,或者它们叫什么。

我已经挖得很深,很深很浅.. 但似乎没有其他人需要这样做。主要是因为混杂套接字旨在获取流量并对其进行分析。但是.bind((interface, protocol)) 也在那里,它面临着同样的问题,一种不进入混杂模式的方式,而是通过执行.bind((interface, 0x0800)) 来接收 TCP 数据包。

我在这里可能会遇到困难,但也许man 7 netdevice 只是给了我一个想法。我正在尝试设置SystemTap 来检查调用ioctl() 的功能以及socket() 对象如何请求文件描述符。可能是关于这一切如何发生的线索。让 SystemTap 工作很棘手。

任何人有任何其他线索如何解决这个问题或以前遇到过这个问题?

附言。抱歉,这个问题很模糊,我不知道这些低级事物的正确术语是什么。因为它们对我来说很新。

编辑:我可能一直在寻找 bind() 的错误位置,根据 ipv4.af_inet 实现它会尝试调用套接字 bind() 函数,但如果不是,它会尝试设置很多魔法在这里。和here 他们在af_inet 上铿锵作响,做了很多桌子垃圾。我还没有找到解决方案,但可能会在路上迈出一步……或者最坏的情况,又一次打鹅。

进一步深入兔子洞,selinux/hooks.c 也包含一些绑定功能。也许更多的安全相关,但仍然值得我调查。仍然没有解决这个该死的谜题。

【问题讨论】:

  • 我认为您的问题没有“适当”的解决方案。内核将为它实现的协议(TCP/UDP、ICMP、SCTP 等)处理传入的数据包,并且除了安装 iptables 过滤器之外,没有旋钮/ioctl/socketoption/api 调用来关闭该处理。跨度>
  • @nos 我的观点是,已经实现的协议处理让我们说 TCP 或 UDP,有一种方法可以告诉内核不要对某个端口进行处理。内核如何跟踪某个应用程序(在这种情况下,它更像是某个地方的注册文件描述符) - 可以完全控制端口上的任何数据流。我想告诉内核同样的事情,“不要弄乱这个端口, 已经弄清楚了” - 或者类似这样的事情:)
  • 不,不是。协议处理(例如 TCP 或 UDP)在内核中处理,而不是由应用程序或任何套接字库处理。因此,任何地方都没有条目表明应用程序可以完全控制端口。内核中有一个关于例如状态的条目。一个 TCP 端口。如果任何应用程序绑定到特定端口,则发往该端口的 TCP 数据包完全在内核中处理(并且内核处理 TCP 协议后的任何 TCP 有效负载数据都会发送到绑定到该端口/套接字的应用程序。 )
  • @nos 我对编写一个内核模块的想法并不陌生,我可以调用它来做我的竞标。考虑到这一点,这可能吗?知道在哪里寻找如何设置此状态吗?我在 Python 中这样做的唯一原因是对最终产品进行模拟,一个与用户空间通信的内核模块现在可以工作。最终产品很可能必须是内核模块和 c 代码。
  • 我认为模块真的没有任何地方可以影响这一点,至少不是 iptables 无法做到的事情。 TCP 数据包结束并由现有套接字进一步处理的地方,或者发送 RST,因为没有人在监听:elixir.bootlin.com/linux/v4.13/source/net/ipv4/tcp_ipv4.c#L1645

标签: python linux sockets linux-kernel raw-sockets


【解决方案1】:

这里的问题是,您正在寻求一种方法来告诉内核“为我做这 100 件事,但留下一个特定的细节”。坦率地说,我认为 iptables 解决方案是最简单和最干净的。

不过,另一种选择是要求内核完成所有其他工作,而是自己承担更多工作。具体来说,就是自己编一个IP地址,然后开始使用。唯一的缺点是您必须接管内核一直在为您做的另一件重要事情:响应 ARP(ARP 用于发现拥有给定 IP 地址的工作站的 MAC [以太网] 地址)。简而言之,我建议您:

  1. 在本地子网上选择一个未使用的 IP 地址。
  2. 组成一个 MAC 地址供您使用。 (并非绝对必要,但会更容易区分“您的”流量。)
  3. 打开原始数据包套接字而不是原始 IP 套接字 (https://linux.die.net/man/7/packet)。
  4. 编写并发送 ARP 请求以发现您要发送到的站点的 MAC 地址(如果在本地 LAN 上,则为下一跳 [路由器] IP 地址的 MAC)。
  5. 接收ARP回复并记录对方的MAC。
  6. 构造并从您自己的 MAC 地址发送您的 SYN 数据包到目标站的 MAC。 (使用您选择的源和目标 IP、端口等)
  7. 侦听您的 IP 的返回 ARP 并根据需要进行回复。
  8. 接收 SYN+ACK 响应。由于内核不知道目标 IP 地址(您编造的 IP 地址)属于您的系统,因此内核不会使用 RST(或其他任何东西)响应 SYN+ACK。
  9. 接下来想做什么就做什么……

如果您使用分配给接口的 MAC 地址以外的 MAC 地址,您当然必须进行混杂捕获。这对于原始数据包套接字来说非常典型。此外,您将为所有流量构建以太网标头、IP 标头和 TCP 标头(好吧,为 ARP 请求构建以太网 + ARP),因此您将学到很多东西。

【讨论】:

  • 我听到了,但我大脑的逻辑部分是“传统套接字(TCP、UDP 等)没有告诉内核改变一个端口行为的功能”。我知道我问的一般是低效的,你可能是对的,没有一个神奇的功能可以做这些事情。但是,如果有针对特定端口的注册应用程序,内核会在哪里跟踪呢?以及如何执行。我很乐意修改和构建自定义内核来调整这种需求。旁注:我的“switch.py​​”已经实现了 ARP,所以这意味着不需要额外的努力。
  • 那里用了很多词,我喜欢你自己编IP地址的想法,实际上一点也不差!我不能强调这种解决方法有多聪明。一切都不是我想要的哈哈。但它确实以一种相当独特的方式解决了这个问题。我已经构建了以太网、IP 和 TCP 标头。除了 ARP/ICMP/OSPF 支持之外,仅举几例。我会突然需要支持 DHCP 数据,但这是可以管理的哈哈。我会先尝试我最初的思路,看看我是否能以这种方式解决它,如果没有其他办法 - 这就是解决标记!
猜你喜欢
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
  • 2017-11-24
  • 2012-08-17
  • 2012-01-26
  • 2015-05-14
  • 2013-12-12
相关资源
最近更新 更多