【问题标题】:reading both tcp and udp packets from same socket从同一个套接字读取 tcp 和 udp 数据包
【发布时间】:2018-12-25 05:56:21
【问题描述】:

我正在尝试在路由器中读取数据包,例如在 python 中:

# (skipping the exception handling code here)    
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
while True:
    p = s.recvfrom(2000)
    pkt = p[0]
    # process pkt here ...

对相关问题 (36115971) 的回答说 UDP 与 TCP 数据的参数和方法不同(有人说 recv 用于 TCP,recvfrom 用于 UDP,而其他人则相反,同样有人说 1024 作为缓冲区TCP 的大小和 UDP 的更大,还有人说相反)。在我在路由器中读取的情况下,我没有用于 TCP 和 UDP 的不同套接字,所以我需要从同一个套接字读取两者,所以我对如何读取传入的数据包有点困惑。

(1) 如果我想同时读取 TCP UDP 数据包,我应该使用 recv() 还是 recvfrom()?

(2) 调用是一次返回一个数据包,还是在缓冲区填满后返回?例如,如果我有一个 4096 字节的大缓冲区,并且传入的 2 个数据包每个有 2400 字节,那么调用会在第一个数据包结束后立即返回,还是在第二个数据包填满缓冲区后返回?

(2a) 同样的问题,但如果我有一个 2000 字节的较小缓冲区。很明显,在第一次调用时,我将获得第一个数据包的前 2000 个字节。但是在下一次调用时,我会得到第一个数据包的最后 400 个字节,还是第二个数据包的前 2000 个字节?

(3) 如果我在拨打下一个电话时被延迟,可能是因为我正忙于处理第一个数据集,我是否有丢失数据的危险,或者操作系统是否会保留其内部的传入数据包队列以提供给我下次打电话的时候?如果操作系统保留其内部队列,我在哪里可以找到有关其大小的信息?

注意:一些给定的答复存在分歧,所以让我对我的问题进行一些限制。希望这些限制将有助于给出更具体的答案。

(a) 我的目标是使用 python 套接字 嗅探传入的数据包。所以其他涉及 tcpdump 或 tshark 等的解决方案不在此范围内。

(b) 目标是仅嗅探传入的数据包。数据包重新排序(对于 TCP 等面向连接的协议)等其他细节不在范围之内,实际上它们是可以避免的开销。

【问题讨论】:

  • 您可以按照您的描述进行操作,但我认为您必须定义两个绑定在同一个端口上的不同套接字,一个用于 TCP,一个用于 UDP。虽然如果您打算进行数据包级别检查,我会提出一个类似scapy 的框架,它允许您检查数据包是TCP 还是UDP。另请查看可能有用的this answer。另外,在Network Engineering 上问这个问题可能会更好。
  • 感谢您的建议。我也在网络工程论坛上发布了这个问题 - networkengineering.stackexchange.com/questions/51862
  • @game0ver,我们不回答这样的网络工程问题
  • @MikePennington 好的,对不起,我不知道,我只是想,因为它与网络有很大关系,它可能更适合那里......

标签: python sockets networking tcp udp


【解决方案1】:

如果您从原始套接字读取数据包(如您的源代码所示),那么您可以轻松地从同一个套接字读取所有数据包。确保这是您打算做的。原始套接字用于为故障排除、取证、安全或教育目的进行数据包检查。您无法通过这种方式轻松地与另一个系统进行通信。

同样,这里的接收调用不会因协议而异,因为您实际上没有使用 TCP 或 UDP,您只是接收这些协议构建和解码的原始数据包。

(1) 如果我想同时读取 TCP 和 UDP 数据包,我应该使用 recv() 还是 recvfrom()?

任何一个都可以。 recv() 将只返回实际数据包数据,而recvfrom 将返回数据以及有关数据包的元数据,包括接收数据的接口(以及struct sockaddr_ll 中定义的其他内容) packet(7) 手册页)。

(2) 调用是一次返回一个数据包,还是在缓冲区填满后返回?例如,如果我有一个 4096 字节的大缓冲区,并且传入的 2 个数据包每个有 2400 字节,那么调用会在第一个数据包结束后立即返回,还是在第二个数据包填满缓冲区后返回?

当使用这样的原始套接字时,您一次只能得到一个数据包。你永远不会得到超过一个。如果您提供的缓冲区不够大,则数据包将被截断(丢弃结束字节)。

(2a) 同样的问题,但如果我有一个 2000 字节的较小缓冲区。很明显,在第一次调用时,我将获得第一个数据包的前 2000 个字节。但是在下一次调用时,我会得到第一个数据包的最后 400 个字节,还是第二个数据包的前 2000 个字节?

一般来说,大多数网络上的数据包被限制在大约 1514 字节。这是因为在网络接口上配置的传统“MTU”(最大传输单元)是 1500 字节,并且通常包含两个 MAC 地址(每个 6 字节)加上两个字节的 Ethertype 的以太网报头。在交换机或路由器中,您可能还会看到具有附加 4 字节标头的数据包,其中包含 VLAN 标头 (IEEE 802.1Q)。 (但是,一些网络在内部使用最大约 9K 的“巨型”数据包用于特定目的。)

您还应该了解,在编写应用程序时,可以发送大于最大数据包大小的 UDP 数据报(或 TCP 缓冲区)。在这种情况下,操作系统将它们分解成更小的块以进行发送(并且它们在传递给应用程序之前在目标端重新组装)。当您接收到这样的原始数据包时,您会看到处于低级别(可能是碎片化)状态的数据包。

(3) 如果我在拨打下一个电话时被延迟,可能是因为我正忙于处理第一个数据集,我是否有丢失数据的危险,或者操作系统是否会保留其内部的传入数据包队列以提供给我下次打电话的时候?如果操作系统保留其内部队列,我在哪里可以找到有关其大小的信息?

操作系统将为您保留一个数据包队列。大小当然是有限的,因为你无法跟上全线速的 1Gb NIC(更不用说 10Gb 或更高的 NIC)了。大小以系统特定的方式配置。在 linux 上——可能还有其他基于 Unix 的系统——你可以调用 getsockoptSOL_SOCKET / SO_RCVBUF 来了解可用的队列空间。

至少在 linux 上,可以使用setsockopt 将大小设置为系统强加的最大值(它本身可以使用各种sysctl 设置进行配置)。

【讨论】:

  • 很难知道 OP 到底是什么意思; “试图读取数据包”是明确的目标。可能是 OP 期望能够打开一个原始套接字并深入其中以直接采样任意 TCP 数据流。但是,只有当 OP 打算 terminate 盒子上的连接时,打开本机 TCP 套接字才有用。鉴于它是“在路由器中”完成的,这似乎不太可能。无论如何,我认为我的陈述是适当的。
  • 我很清楚tcpdump(尽管如果你真的想在程序中读取数据包,你会使用libpcap)。如果您的目的是教育或实验,那么使用 python 是完全有效的。而且,顺便说一句,甚至还有一个libpcap 也适用于python:pypi.org/project/libpcap
  • 感谢 Gil 的详细回答,感谢 Mike 的 cmets。我对我的问题添加了一些限制,希望这将有助于解决冲突。
  • 如果你对在 Python 中捕获所有数据包一无所知,那么这个答案上的 cmets 足以将你带到你想去的地方。我坚持认为 tcpdump 是正确的解决方案,在 Python 中这样做的唯一合理原因是为了完成学校作业。
  • @Mike:这不是学校作业 :) 限制是我必须在 python 中实现,并且通过避免不必要的开销来非常高效。所以这对我来说似乎是最好的解决方案。感谢您的帮助。
【解决方案2】:

我认为你不应该那样做,因为 TCP 保证了各种事情,比如可靠性、排序、流量控制和拥塞。但是 UDP 不保证任何事情。

这些参数是在操作系统创建套接字时定义的。这就是为什么我认为你不能做到你所说的。

打开两个不同的套接字,一个本地 UDP sock 和一个本地 TCP sock。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 2014-05-14
    • 2012-06-17
    • 2017-04-04
    • 1970-01-01
    • 2018-04-03
    • 2011-01-05
    相关资源
    最近更新 更多