使用原始套接字的在以太网中的抓包

Raw socket的应用

1、特点

TCP/UDP类型的套接字只能够访问传输层以及传输层以上的数据,因为当IP层把数据传递给传输层时,下层的数据包头已经被丢掉了.而原始套接字却可以访问传输层以下的数据,,所以使用raw套接字你可以实现上至应用层的数据操作,也可以实现下至链路层的数据操作.

2、创建

       int sockfd;

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

Socket函数的三个参数:

AF_INET 常用socket的协议类型,使用TCP或UDP来传输,用在IPv4的地址

AF_INET6 与上面类似,不过是来用在IPv6的地址

AF_UNIX 本地协议,使用在Unix和Linux系统上,它很少使用,一般都是当客户端和服务器在同一台及其上的时候使用

SOCK_RAW 原始套接口,可访问低层次协议,如链路层数据。

SOCK_STREAM 流式套接口,这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
SOCK_DGRAM 数据包式套接口,这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。

IPPROTO_ICMP 指定协议类型ICMP.也可以指定为IPPROTO_TCP、IPPROTO_UCP等。

socket(AF_INET,SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ARP|ETH|RARP))

套接字在数据链路层直接抓取以太网帧,其中第二个参数有2种,若为SOCK_RAW,那么抓取到的数据没有经过系统处理,完整的保留有以太网帧头,若为SOCK_DGRAM那么以太网帧头会被自动处理掉。

第三个参数的协议类型有:

ETH_P_IP 0x0800只接收发往本机mac的ip类型的数据帧

ETH_P_ARP 0x0806只接受发往本机mac的arp类型的数据帧

ETH_P_RARP 0x8035只接受发往本机mac的rarp类型的数据帧

ETH_P_ALL 0x0003接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)

3、使用

(1):通过原始套接字来访问TCP/UDP或者其它类型的数据,调用socket函数创建原始套接字第三个参数应该指定为htons(ETH_P_IP),也就是通过直接访问数据链路层来实现.

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))//可直接访问链路层

(2):对于ICMP和EGP等使用IP数据包承载数据但又在传输层之下的协议类型的IP数据包,内核不管是否已经有注册了的句柄来处理这些数据,都会将这些IP数据包复制一份传递给协议类型匹配的原始套接字.

(3):对于不能识别协议类型的数据包,内核进行必要的校验,然后会查看是否有类型匹配的原始套接字负责处理这些数据,如果有的话,就会将这些IP数据包复制一份传递给匹配的原始套接字,否则,内核将会丢弃这个IP数据包,并返回一个ICMP主机不可达的消息给源主机

(4)如果原始套接字没有调用bind和connect函数,则核心会将所有协议匹配的IP数据包传递给这个原始套接字.


2).编程选项
原始套接字是直接使用IP协议的非面向连接的套接字,在这个套接字上可以调用bind和connect函数进行地址绑定.说明如下:

(1)bind函数:调用bind函数后,发送数据包的源IP地址将是bind函数指定的地址。如不调用bind,则内核将以发送接口的主IP地址填充IP头. 如果使用setsockopt设置了IP_HDRINCL(header including)选项,就必须手工填充每个要发送的数据包的源IP地址,否则,内核将自动创建IP首部.

(2)connect函数:调用connect函数后,内核将会用这个绑定的地址填充IP数据包的目的IP地址可以使用write和send函数来发送数据包否则的话,则应使用sendto或sendmsg函数来发送数据包,并且要在函数参数中指定对方的IP地址

综合以上种种功能和特点,我们可以使用原始套接字来实现很多功能,比如最基本的数据包分析,主机嗅探等.其实也可以使用原始套接字作一个自定义的传输层协议.


/***************SimpelSniffer.c*************/
 
//auther:duanjigang@2006s
 
#include  
#include 
#include  
#include 
#include 
#include 
#define BUFFER_MAX 2048
 

int main(int argc, char *argv[])
 
{
        int sock, n_read, proto;        
        char buffer[BUFFER_MAX];
 
        char  *ethhead, *iphead, *tcphead, 
                         *udphead, *icmphead, *p;
 
        
if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
 
    {
 
        fprintf(stdout, "create socket error\n");
 
        exit(0);
 
    }
 
        
while(1) 
{
 
     n_read = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
 
        /*
 
        14   6(dest)+6(source)+2(type or length)
 
        +
 
        20   ip header 
        +
 
        8   icmp,tcp or udp header
 
        = 42
 
        */
 
if(n_read < 42) 
   {
 
      fprintf(stdout, "Incomplete header, packet corrupt\n");
 
      continue;
 
   }
 
                
        ethhead = buffer;
 
        p = ethhead;
 
        int n = 0XFF;
 
                printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
 
                           "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",
 
        p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,
 
        p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);
 

                   iphead = ethhead + 14;  
 
                   p = iphead + 12;
 
        
 
           printf("IP: %d.%d.%d.%d => %d.%d.%d.%d\n",
 
           p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,
 
           p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);
 
            proto = (iphead + 9)[0];
 
            p = iphead + 20;
 
             printf("Protocol: ");
 
            switch(proto)
 
              {
 
                case IPPROTO_ICMP: printf("ICMP\n");break;
 
                case IPPROTO_IGMP: printf("IGMP\n");break;
 
                case IPPROTO_IPIP: printf("IPIP\n");break;
 
                case IPPROTO_TCP :
 
                case IPPROTO_UDP : 
    printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP"); 
    printf("source port: %u,",(p[0]<<8)&0XFF00 |  p[1]&0XFF);
 
    printf("dest port: %u\n", (p[2]<<8)&0XFF00 | p[3]&0XFF);
 
         break;
 
    case IPPROTO_RAW : printf("RAW\n");break;
 
    default:printf("Unkown, please query in include/linux/in.h\n");
 
        }
 
   }
 
}


借鉴了大神的文章,觉得写得很不错,对我很有帮助,也实现了我想要的功能,故此和大家分享。

感谢原作者!http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=876233

 

你可能感兴趣的:(Linux)