发送带有效负载的TCP SYN数据包

8
在TCP连接初始化时,是否可以发送带有自定义有效负载的SYN数据包?我的直觉是理论上是可行的。我正在寻找在Linux中实现此目标的简单方法(使用C或Go语言),但由于这不是标准行为,因此我尚未找到有用的信息。(这篇文章相似,但并不是很有帮助。)
请帮帮我,谢谢!
编辑:对于这个任务的可能性,我还在寻找一种方法,甚至是示例代码。

1
你提到的duplicate表示这是不可能的。你可能不认为这很有帮助,但实际上它是有用的。 - user207421
@EJP,我对那篇文章感到困惑,因为另一个答案说“在SYN数据包中发送数据是可能的”。我猜问题在于缩放窗口尚未建立。所以你知道TCP滑动窗口是否也会处理握手数据包(SYN、SYNACK和ACK)?还是它只关心实际的数据包? - zzy
1
如果你编写自己的TCP协议,那么这是可能的;否则不行。你可以通过原始套接字单独发送这样的数据包,但是你不能通过这种方式进行整个TCP会话,因为内核会阻止你。TCP滑动窗口关注的是数据,而SYN和ACK本身不是数据(尽管ACK可以附加在数据上)。 - user207421
@EJP 好的,我明白了。非常感谢! - zzy
4个回答

6
据我所了解(并根据Jeff Bencteux在另一个答案中的评论),TCP Fast Open针对TCP解决了这个问题。
请参阅this LWN article
引用:

消除一次往返

从理论上讲,初始的SYN段可以包含由连接发起者发送的数据:RFC 793(TCP规范)允许在SYN段中包含数据。但是,在三次握手完成之前,TCP禁止将该数据传递给应用程序。

...

TFO的目标是通过允许将数据作为启动连接的SYN段的一部分包含来从TCP会话中消除一次往返时间。

enter image description here


1
请注意,您的示例仅在使用TCP cookies时才有效。如果没有任何TCP cookies,则服务器不允许发送数据包,直到它从客户端收到ACK,因为这意味着欺骗性的初始TCP数据包可能会将服务器用作放大攻击。 - Ferrybig

5
显然,如果你在两端编写自己的软件,那么可以按照你想要的方式使其工作。但是,如果你依赖于任一端上的标准软件(例如,标准 Linux 或 Windows 内核),那么不行,因为根据 TCP 协议,只有在会话建立后才能发送数据,并且会话要等到从对等方获得对你的 SYN 的确认(acknowledgment)时才能建立。
例如,如果你向一个 Linux 内核发送同时包含附加有效载荷的 SYN 数据包(注意:这在某种程度上是猜测,因为我实际上没有尝试过),它将简单地忽略有效载荷并继续确认(SYN/ACK)或拒绝(用 RST)SYN,具体取决于是否有监听器。
无论如何,你可以尝试这样做,但由于你将离开“保护区”,所以你需要制定自己的原始数据包;你不能让本地操作系统为你创建它们。
Python 的 scapy 包可以构造它:
#!/usr/bin/env python2
from scapy.all import *
sport = 3377
dport = 2222
src = "192.168.40.2"
dst = "192.168.40.135"
ether = Ether(type=0x800, dst="00:0c:29:60:57:04", src="00:0c:29:78:b0:ff")
ip = IP(src=src, dst=dst)
SYN = TCP(sport=sport, dport=dport, flags='S', seq=1000)
xsyn = ether / ip / SYN / "Some Data"
packet = xsyn.build()
print(repr(packet))

谢谢!我明白这不是协议的一部分,我也不能依赖标准的东西。我认为理论上是可行的。但我的问题实际上是“如何做到?”(对于这里的歧义表示抱歉,我会编辑问题以使其更清晰)。根据您的建议,问题可能是“如何从原始数据包中制作带有有效负载的SYN数据包?” 有任何建议或示例代码吗? - zzy
仅凭猜测,没有阅读规格说明书:任何体面的路由器(特别是NAT/PAT路由器)在读取到这样的“畸形”数据包时,都会简单地丢弃该数据包并重置连接。 - CodeCaster
3
如果你使用的是标准的Linux或Windows内核,那么不,这是不可能的。但是,你可以使用TCP Fast Open选项来实现它,这是一种实验性的选项,但Linux已经实现了它(请参考 proc/sys/net/ipv4/tcp_fastopen进行配置)。然而,这并不是最优的选择,因为你需要建立第一个连接才能使用它。 - Jeff Bencteux
1
@zzy,我觉得你误解了TFO :). Cookie的长度为4到16个字节。但是当你通过选项发送cookie时,你还会在SYN的有效负载中发送数据。这就是目标。然而,你需要先向另一端点请求一个cookie,所以只有在建立第一次连接之后才能这样做。 - Jeff Bencteux
只是要补充一点,虽然这是“可能”的,但如果涉及到防火墙,它几乎肯定不会起作用。普通家用路由器,甚至AWS安全组可能没有这种复杂性,但有些防火墙将过滤SYN数据作为一个特性(例如https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClT5CAK)。它们只会悄悄地丢弃有问题的数据包,因此建立连接将似乎超时。 - Ralph Bolton
显示剩余4条评论

1
TCP Fast Open可以做到这一点。但是双方都应该支持TCP Fast Open。QUIC是一种新协议,基于0-RTT来解决这个问题。

1

我之前说过这是不可能的。从一般意义上来说,我仍然坚持这个评估。

但是对于客户端来说,使用connect() API实际上是不可能的。在使用TCP Fast Open时,有一种替代的连接API。示例

    sfd = socket(AF_INET, SOCK_STREAM, 0);
    
    sendto(sfd, data, data_len, MSG_FASTOPEN, 
                (struct sockaddr *) &server_addr, addr_len);
        // Replaces connect() + send()/write()
    
    // read and write further data on connected socket sfd

    close(sfd);

没有API允许服务器将数据附加到发送给客户端的SYN-ACK。

即使如此,如果您只需要来自客户端的数据,则在客户端和服务器上启用TCP Fast Open可能会使您实现所需的结果,但它有自己的问题

如果您想要与TCP相同的可靠性和数据流语义,您将需要一个新的可靠协议,该协议除了TCP提供的拥塞控制和窗口缩放之外,还具有初始数据段,例如。

幸运的是,您不必从头开始实现。 UDP协议是一个很好的起点,可以作为您的新L4的L3。

其他项目已经做过类似的事情,因此可能可以使用那些而不是实现自己的项目。考虑QUICUDT。这些协议是在现有的UDP协议上实现的,因此避免了部署TCP Fast Open时面临的问题。


我可以问一下TCP滑动窗口是否也处理握手包(SYN、SYNACK和ACK)吗?我之前认为它只处理实际的数据包。 - zzy
@zzy,我在谈论有用的TCP功能,这些功能应该是替代它的协议的一部分。 - jxh
问题是是否可以在SYN数据包中发送数据 - 这是完全可以做到的。这是非标准的,因此可能会被一些防火墙过滤,但如果您想要这样做,您可以这样做。您不需要使用UDP或其他协议。 - Ralph Bolton
@RalphBolton:我已经澄清了我的答案,并特别提到了TCP Fast Open。谢谢。 - jxh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接