2012年(1)
分类:
2012-05-14 12:37:00
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
一般情况下,我们使用raw socket都是用于发送icmp包或者自己定义的包。最近我想用raw socket
自己去创建TCP的包,并完成3次握手。
在这个过程中,发现了几个问题:
1. 发现收不到对端返回的ACK,但是通过tcpdump却可以看到。这个通过修改创建socket的API参数得以纠正。
原来创建socket使用如下参数s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)。因为linux的raw socket不会把
不会把UDP和TCP的分组传递给任何原始套接口。——见《UNIX网络编程》28.3.
所以为了读到ACK包,我们创建的raw socket需要建立在数据链路层上。
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))
这样该socket就可以读取到所有的数据包。当然这样的数据包无疑是非常庞大的,那么我们可以
使用bind和connect来过滤数据包。bind可以指定本地的IP和端口,connect可以指定对端的IP和端口。
2. 在修改完创建socket的API,我发现还有一个问题,到现在依然没有找到合适的解决方法。
通过链路层的raw socket,我们可以收到对端的ACK。但是因为linux内核也会处理TCP的握手过程,
所以,当它收到ACK的时候,会认为这是一个非法包,会直接发送一个RST给对端。这样我们拿到了这个ACK,
也没有实际的用处了——因为这个连接已经被RST了。
我想,内核之所以会直接发送RST,也许是因为我们创建的是raw socket,但是发送的确是TCP包,
当对端返回ACK时,内核根本不认为我们已经打开了这个TCP端口,所以会直接RST掉这个连接。
那么问题就在于,我们如何告诉内核,我们打开了TCP的这个端口。
我想过一个方法,除了一个raw socket,再创建一个真正的TCP socket。但是细想起来,这条路应该行不通。
在网上搜了半天,总算找到一个比较详细的解释了。原因给我猜想的相差不远,当我们使用raw socket来发送sync包时,内核的TCP协议栈并不知道你发送了sync包,所以当对端返回SYNC/ACK时,内核首先要处理这个包,发现它是一 个SYNC/ACK,然而协议栈却不知道前面的sync,所以就认为这个包是非法的,于是就会发送RST来中止连接。
原文如下:
This is one of the most frequently asked question by someone who is
experimenting with raw sockets and TCP/IP. It is known that the
'IP_HDRINCL' socket option allows you to include the IP header along
with the data. Since TCP encapsulates the IP header, we can also build
a TCP packet and send it over a network. But the problem is, a TCP
connection can never be established this way. The scenario is as
follows:
A TCP connection is always made by a three-way handshake.
So, initially you send a 'SYN' packet to the remote machine. If it is
actively listening on the port, you get a 'SYN/ACK' packet. So far so
good. But before you can respond, your machine sends an 'ACK/RST'
packet and connection attempt is ended. For the connection to be
complete, instead of the 'RST' packet, your machine should be sending
an 'ACK' to the remote machine.
The difference lies where the
connection is exactly made. Although the programs are communicating
after the connection is complete, the TCP connection is never between
two programs but rather between the TCP stacks of the two machines.
Here 'stack' means a layer of programs that communicates between each
other. TCP stack stands for the protocol driver or the actual network
transport protocol. Now lets look at exactly what happens when you send
a 'SYN' packet...
Since you are using raw sockets ('SOCK_RAW') and
not TCP/Stream sockets ('SOCK_STREAM') the TCP stack has no information
about what you are doing at program level. And since the 'IP_HDRINCL'
allows you to build any type of IP packet and send it along with the
data, you can build a 'SYN' packet and send it to the TCP server
program which is actively listening. But the point is that the 'SYN'
packet is being sent from your program and not the stack. In other
words the TCP stack of your machine has no idea how of sending the
'SYN' packet.
On the other side the 'SYN' packet is received by the
stack at the remote machine and not exactly by the program. As with the
case of the arrival of any 'SYN' packet, the stack at the remote
machine responds with a 'SYN/ACK' packet. This packet is now received
by the TCP stack of your machine. In other words, the incoming TCP
packet ('SYN/ACK') will be processed by the stack. Since it has no
information of the previous sent 'SYN' packet, it responds with a 'RST'
packet, as in the case of any improper or unacceptable packet for a
connection.
So the difference between sending and receiving a TCP
packet using raw sockets is, the former is not processed while the
latter is processed by the TCP stack of your machine.
该解释来自于,
感谢Mr Andreas Masur 和Mr Mathew Joy. Thanks Mr Andreas Masur and Mr Mathew Joy.
作者:gfree.wind@gmail.com
hysteria_zero2014-02-22 21:24:27
我英文很烂,经过google翻译,这段英文似乎也并没有说明该如何解决。
请问博主解决了系统自动回复RST的问题了吗?
是怎么解决的的?
谢谢!