Chinaunix首页 | 论坛 | 博客
  • 博客访问: 21583
  • 博文数量: 26
  • 博客积分: 1000
  • 博客等级: 少尉
  • 技术积分: 270
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-27 11:08
文章分类

全部博文(26)

文章存档

2010年(26)

我的朋友
最近访客

分类: LINUX

2010-07-15 17:08:33

一般情况下,我们使用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.

 

---------------------

有关raw socket的一些知识

    众所周知,通过socket编程,我们能够实现机器之间的通信.在TCP/IP协议
簇(PF_INET)中,可以建立面向连接的SOCK_STREAM类型的socket,非连接的
SOCK_DGRAM类型的socket.事实上,在所有的网络程序中,也是这两种socket用
的最为广泛.除此之外,还有一些不常用的socket类型,它们却是在某些网络通
信中担当重要的角色.这里要讲的就是这么一种socket,称之为raw socket.
    raw socket的作用主要在三个方面:
    1.通过raw socket来接受发向本机的ICMP,IGMP协议包,或者用来发送这些
    协议包.
    2.接受发向本机的但TCP/IP栈不能够处理的IP包.
    3.用来发送一些自己制定源地址特殊作用的IP包(自己写IP头,TCP头等等)

    我们知道,平时我们想看一看网络是否通达,就用ping命令测试一些.ping
命令用的是ICMP协议.因此,我们不能够通过建立一个SOCK_STREAM或SOCK_DGRAM
来发送这个包,只能够自己亲自来构建ICMP包来发送.这是一种情况.另一种情况
是:现在许多操作系统在实现网络部分的时候,通常只实现了常用的几种协议,
如tcp,udp,icmp等,但象其它的如ospf,ggp等协议,操作系统往往没有实现,如果
自己有必要编写位于其上的应用,就必须借助raw socket来实现,这是因为操作
系统遇到自己不能够处理的数据包(ip头中的protocol所指定的上层协议不能处
理).就将这个包交给raw socket.而最后一种使用raw socket的目的主要是用来
构建一些特殊的协议头,比如我们想对某台机器进行denial of service类型的
攻击,但是有不想留下痕迹,让别人知道IP包的来源,这时候就可以使用raw
socket来发送这些伪造源地址信息的包,这其实也是这种攻击所采用的主要技术
手段.当然了,我说的是HACKER行为,之所以想要处理这些特殊的IP包,通常也是
为了诊断网络的目的.
   
    raw socket的建立是通过如下方式的:
   
    sockfd = socket(PF_INET, SOCK_RAW, protocol);

    第一个参数就不必讲了,第二个参数说明建立的是一个raw socket,第三个
参数倒是需要详细解说一下.这里分三种情况:
    1.参数protocol用来指明所要接收的协议包,如果是象IPPROTO_TCP(6)这
    种非0,非255的协议,则内核碰到ip头中protocol域和创建socket所使用参
    数protocol相同的IP包,就会交给这个rawsocket来处理.因此,一般说来,
    要想接收什么样的数据包,就应该在参数protocol里来指定相应的协议.当
    内核向此raw socket交付数据包的时候,是包括整个IP头的,并且已经是重
    组好的IP包. 如下:

    ---------------------------------------------------------------
    |ip header|tcp header(or x header)|             data          |
    ---------------------------------------------------------------        
    用recvfrom收到的数据包括一个IP头,一个相应的协议头,然后是数据(数
    据也可以为空,就看实际情况了). 但当我们发送IP包的时候,却不用亲自
    处理IP包头,只需要填 充参数protocol所指定的相应的协议头即可.也就
    是说,用sendto的时候,我们提供给它的缓冲区数据是从IP包头的第一个字
    节开始,如下,只需要构造这么一个缓冲区就可以了.

    --------------------------------------------------------------
    |tcp header(or udp header or x header)|           data       |
    --------------------------------------------------------------
    如果想自己也想亲自处理IP头,则需要IP_HDRINCL的socket选项.如下:

    int flag = 1;
    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(int));

    这样,发送时所要提供的缓冲区有成如下形式:

    ---------------------------------------------------------------
    |ip header|tcp header(or x header)|             data          |
    ---------------------------------------------------------------        
    但是,即时是这种情况,在我们发送IP包的时候.也不是填充ip头的所有字
    段,而是应该将ip头的id(identification)字段设置为0,表示让内核来处
    理这个字段.同时,内核还帮你完成ip头的校验和的计算,并随后填充check
    字段.

    2.如果protocol是IPPROTO_RAW(255),这时候,这个socket只能用来发送
    IP包,而不能接收任何数据.发送的数据需要自己填充IP包头,并且自己计
    算校验和.
    3.对于protocol为0(IPPROTO_IP)的raw socket. 在linux和sco unix上
    是不允许建立的.我没有再试过其他操作系统,谁能给我一个答案:)

    对于raw socket,只有root权限才能够创建.
    这里对raw socket总结一下: 当内核接收到IP包的时候,首先检查ip包的
protocol域,当存在与此域匹配的raw socket时,就将包先传给此raw socket,
然后交给相应的上层协议处理.交给raw socket的数据是包括IP头并且已经重
组完成后的.当使用raw socket发送包的时候,如果raw socket创建时的protocol
不是0或255,并且没有设置IP_HDRINCL选项,则发送的数据不包括IP头.如果此
选项置位,则需要自己构件IP头.如果创建protocol为255的raw socket,此raw
socket只能用来发送包括IP头,自己构建IP包.


附录:
IP头结构:

struct iphdr

|-------|--------|---------------|-------------------------------|
|version|   ihl  |    tos      |           tot_len   |
|-------|--------|---------------|-------------------------------|
| id    |              |       frag_off |
|----------------|---------------|-------------------------------|
|      ttl       | protocol   |        check          |
|----------------|---------------|-------------------------------|
|                             saddr                              |
|----------------------------------------------------------------|
|       daddr                   |
|----------------------------------------------------------------|

参考书籍:
Unix Network Programming (Volume 1 SECOND EDITION P655-P702)

http://blog.csdn.net/caimouse/archive/2007/05/04/1596336.aspx

 

 

阅读(384) | 评论(0) | 转发(0) |
0

上一篇:D和Z

下一篇:raw socket相关(二)

给主人留下些什么吧!~~