zoukankan      html  css  js  c++  java
  • ARP协议(4)ARP编程

    之前的几篇文章,分别介绍了 ARP 协议格式,在vs2012里配置winpcap环境,我们该做的准备都已经做完了。如今我们真正来实现了。


    一、定义数据结构

    依据ARP的协议格式,设计一个ARP协议格式


    依据ARP的分组格式,我们知道它有两部分组成:

    1、以太网首部,这是数据包在数据链路层上传输所必不可缺的部分。它的后面跟着相关的协议数据包(ARP/IP等)

    2、ARP数据包

    所以。我们有这么几个数据结构:

    // 以太网的首部
    typedef struct EthHead
    {
     u_char dest_mac[6];	 // 以太网的目的地址
     u_char src_mac[6];	 // 以太网的源地址
     u_char type[2];	 // 帧类型 ARP:0x0806
    }EthHead;
    // ARP数据包
    typedef struct ArpMsg
    {
     u_char mac_type[2];	 // 硬件类型 以太网: 1
     u_char protocal_type[2];	// 协议类型 IP地址:0x0800
     u_char mac_len;	 // 硬件地址长度 6
     u_char protocal_len;	// 协议地址长度 4
     u_char op[2];	 // 操作字段 ARP请求:1 ARP应答:2 RARP请求:3 RARP应答:4
     u_char sender_mac[6];	// 发送端以太网地址
     u_char sender_ip[4];	// 发送端IP地址
     u_char target_mac[6];	// 目的以太网地址
     u_char target_ip[4];	// 目的IP地址
    }ArpMsg;
    // 以太网ARP
    typedef struct Arp
    {
     EthHead eth_head;
     ArpMsg arpmsg;
    }Arp;

    每一个数据结构都有凝视说明

    注:

    【1】因为网络传输数据都是大端模式,所以在设计这些数据结构,以及在封装数据的时候,注意要把本地序转为网络序

    【2】为了方便,我把全部的字段类型都设为u_char(unsigned char)型。在封装数据的时候,仅仅要按字节赋值。就不须要在进行本地序和网络序的转换了(假设是多字节存储,如short、int等。切记要进行转换)


    二、组包

    /*
    	函数名: PacketArp
    	功	能: 封装ARP包
    	參  数:
    		arp_req  : Arp类型,出參
    		op		 : 操作字段
    				   ARP请求:1
    				   ARP响应:2
    				   RARP请求:3
    				   RARP对应:4
    		dest_mac : 以太网目的地址
    		src_mac	 : 以太网源地址
    		sender_mac:发送端以太网地址
    		sender_ip : 发送端IP地址
    		target_mac: 目的以太网地址
    		target_ip : 目的IP地址
    	返  回: 0正确,-1错误
    
    */
    int PacketArp(Arp *arp_req, u_char op, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    						 u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    	if(arp_req == NULL)
    		return -1;
    	memcpy(arp_req->eth_head.dest_mac, dest_mac, 6);
    	memcpy(arp_req->eth_head.src_mac, src_mac, 6);
    	arp_req->eth_head.type[0] = 0x08;
    	arp_req->eth_head.type[1] = 0x06;
    	//arp_req->eth_head.type = htons(arp_req->eth_head.type);
    
    	arp_req->arpmsg.mac_type[0] = 0x00;
    	arp_req->arpmsg.mac_type[1] = 0x01;
    	arp_req->arpmsg.protocal_type[0] = 0x08;
    	arp_req->arpmsg.protocal_type[1] = 0x00;
    	arp_req->arpmsg.mac_len = 0x06;
    	arp_req->arpmsg.protocal_len = 0x04;
    	arp_req->arpmsg.op[0] = 0x00;
    	arp_req->arpmsg.op[1] = op;
    	memcpy(arp_req->arpmsg.sender_mac, sender_mac, 6);
    	memcpy(arp_req->arpmsg.sender_ip, sender_ip, 4);
    	memcpy(arp_req->arpmsg.target_mac, target_mac, 6);
    	memcpy(arp_req->arpmsg.target_ip, target_ip, 4);
    
    	return 0;
    }
    
    // ARP 请求包
    int PacketArpRequest(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    <span style="white-space:pre">						</span> u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    <span style="white-space:pre">	</span>return PacketArp(arp_req,0x01,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip);
    }
    
    
    // ARP 响应包
    int PacketArpReplay(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    <span style="white-space:pre">						</span> u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    <span style="white-space:pre">	</span>return PacketArp(arp_req,0x02,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip);
    }
    

    三、发包

    /*
    	功能: src_mac 向局域网广播:target_ip 192.168.1.111 的 mac 是多少
    */
    int Text1(Arp *arp)
    {
    	u_char dest_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};// 广播
    	u_char src_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85};// 源mac地址 F0-7B-CB-A3-15-85
    	u_char sender_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85}; // 发送端mac F0-7B-CB-A3-15-85
    	u_char sender_ip[4] = {0xC0,0xA8,0x01,0x65}; //发送端IP地址 192.168.1.101
    	u_char target_mac[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; // 由于不知道mac,所以mac为空
    	u_char target_ip[4] = {0xC0,0xA8,0x01,0x6F}; // 目的端ip 192.168.1.111;
    
    	if(PacketArpRequest(arp,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip) == -1)
    	{
    		printf("Packet arp request error
    ");
    		return -1;
    	}
    	return 0;
    }
     Arp arp;
     if(Text1(&arp) == -1)
     {
      return ;
     }
     memcpy(packet, (void*)&arp, ARP_LEN);
     /* Send down the packet */
     if (pcap_sendpacket(fp, packet, ARP_LEN /* size */) != 0)
     {
      fprintf(stderr,"
    Error sending the packet: %s
    ", pcap_geterr(fp));
      return;
     }
    

    注:pcap_sendpacket 是winpcap里面的发包函数,此函数不支持linux


    至此,我们的ARP组包、发包就完毕了,就是这么简单(winpcap的功劳),以下我们用wireshark来抓包,看我们的包有没有发送出去。


    从抓包来看,我们的包已经正确发出去了(192.168.1.111是我本机的ip),并且从还能够看出,已经有回包了。回包的详细内容:



    我们再来对照下请求包和响应包的差别:



    不言而喻吧


    同一时候。我们再看下我们本地的ARP缓存:



    192.168.1.111这台机器的IP和MAC映射也就增加到ARP快速缓存了。


    附源代码:

    #include <stdlib.h>
    #include <stdio.h>
    
    // pcap_findalldevs_ex
    #define HAVE_REMOTE
    #include <pcap.h>
    
    #define ARP_LEN 42
    
    // 以太网的首部
    typedef struct EthHead
    {
    	u_char dest_mac[6];		// 以太网的目的地址
    	u_char src_mac[6];		// 以太网的源地址
    	u_char type[2];			// 帧类型 ARP:0x0806
    }EthHead;
    
    // ARP数据包
    typedef struct ArpMsg
    {
    	u_char mac_type[2];		// 硬件类型 以太网: 1
    	u_char protocal_type[2];	// 协议类型 IP地址:0x0800
    	u_char mac_len;			// 硬件地址长度 6
    	u_char protocal_len;	// 协议地址长度 4
    	u_char op[2];				// 操作字段 ARP请求:1 ARP应答:2 RARP请求:3 RARP应答:4 
    	u_char sender_mac[6];	// 发送端以太网地址
    	u_char sender_ip[4];	// 发送端IP地址
    	u_char target_mac[6];	// 目的以太网地址
    	u_char target_ip[4];	// 目的IP地址
    }ArpMsg;
    
    // 以太网ARP
    typedef struct Arp
    {
    	EthHead eth_head;
    	ArpMsg arpmsg;
    }Arp;
    
    /*
    	函数名: PacketArp
    	功	能: 封装ARP包
    	參  数:
    		arp_req  : Arp类型,出參
    		op		 : 操作字段
    				   ARP请求:1
    				   ARP响应:2
    				   RARP请求:3
    				   RARP对应:4
    		dest_mac : 以太网目的地址
    		src_mac	 : 以太网源地址
    		sender_mac:发送端以太网地址
    		sender_ip : 发送端IP地址
    		target_mac: 目的以太网地址
    		target_ip : 目的IP地址
    	返  回: 0正确。-1错误
    
    */
    int PacketArp(Arp *arp_req, u_char op, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    						 u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    	if(arp_req == NULL)
    		return -1;
    	memcpy(arp_req->eth_head.dest_mac, dest_mac, 6);
    	memcpy(arp_req->eth_head.src_mac, src_mac, 6);
    	arp_req->eth_head.type[0] = 0x08;
    	arp_req->eth_head.type[1] = 0x06;
    	//arp_req->eth_head.type = htons(arp_req->eth_head.type);
    
    	arp_req->arpmsg.mac_type[0] = 0x00;
    	arp_req->arpmsg.mac_type[1] = 0x01;
    	arp_req->arpmsg.protocal_type[0] = 0x08;
    	arp_req->arpmsg.protocal_type[1] = 0x00;
    	arp_req->arpmsg.mac_len = 0x06;
    	arp_req->arpmsg.protocal_len = 0x04;
    	arp_req->arpmsg.op[0] = 0x00;
    	arp_req->arpmsg.op[1] = op;
    	memcpy(arp_req->arpmsg.sender_mac, sender_mac, 6);
    	memcpy(arp_req->arpmsg.sender_ip, sender_ip, 4);
    	memcpy(arp_req->arpmsg.target_mac, target_mac, 6);
    	memcpy(arp_req->arpmsg.target_ip, target_ip, 4);
    
    	return 0;
    }
    
    // ARP 请求包
    int PacketArpRequest(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    						 u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    	return PacketArp(arp_req,0x01,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip);
    }
    
    // ARP 响应包
    int PacketArpReplay(Arp *arp_req, u_char dest_mac[6], u_char src_mac[6], u_char sender_mac[6],
    						 u_char sender_ip[4], u_char target_mac[6], u_char target_ip[4])
    {
    	return PacketArp(arp_req,0x02,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip);
    }
    
    // 打开网络适配器
    pcap_if_t* choose_interface()
    {
    	pcap_if_t *alldevs;
    	pcap_if_t *d;
    	int inum;
    	int i=0;
    	char errbuf[PCAP_ERRBUF_SIZE];
    
        /* Retrieve the device list on the local machine */
        if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
        {
            fprintf(stderr,"Error in pcap_findalldevs: %s
    ", errbuf);
            exit(1);
        }
        
        /* Print the list */
        for(d=alldevs; d; d=d->next)
        {
            printf("%d. %s", ++i, d->name);
            if (d->description)
                printf(" (%s)
    ", d->description);
            else
                printf(" (No description available)
    ");
        }
    
        if(i==0)
        {
            printf("
    No interfaces found! Make sure WinPcap is installed.
    ");
            return NULL;
        }
        
        printf("Enter the interface number (1-%d):",i);
        scanf_s("%d", &inum);
        
        if(inum < 1 || inum > i)
        {
            printf("
    Interface number out of range.
    ");
            /* Free the device list */
            pcap_freealldevs(alldevs);
            return NULL;
        }
            
        /* Jump to the selected adapter */
        for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
        
        return d;
    }
    
    /*
    	功能: src_mac 向局域网广播:target_ip 192.168.1.111 的 mac 是多少
    */
    int Text1(Arp *arp)
    {
    	u_char dest_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};// 广播
    	u_char src_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85};// 源mac地址 F0-7B-CB-A3-15-85
    	u_char sender_mac[6] = {0xF0,0x7B,0xCB,0xA3,0x15,0x85}; // 发送端mac F0-7B-CB-A3-15-85
    	u_char sender_ip[4] = {0xC0,0xA8,0x01,0x65}; //发送端IP地址 192.168.1.101
    	u_char target_mac[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; // 由于不知道mac,所以mac为空
    	u_char target_ip[4] = {0xC0,0xA8,0x01,0x6F}; // 目的端ip 192.168.1.111;
    
    	if(PacketArpRequest(arp,dest_mac,src_mac,sender_mac,sender_ip,target_mac,target_ip) == -1)
    	{
    		printf("Packet arp request error
    ");
    		return -1;
    	}
    	return 0;
    }
    
    void main(int argc, char **argv)
    {
    	pcap_t *fp;
    	char errbuf[PCAP_ERRBUF_SIZE];
    	u_char packet[ARP_LEN];
    	int i;
        
    	pcap_if_t *d = choose_interface();
    	if(d == NULL)
    	{
    		exit(1);
    	}
    	char *source = d->name;
    
        /* Open the output device */
    	if ( (fp= pcap_open(source,            // name of the device
                            100,                // portion of the packet to capture (only the first 100 bytes)
                            PCAP_OPENFLAG_PROMISCUOUS,  // promiscuous mode
                            1000,               // read timeout
                            NULL,               // authentication on the remote machine
                            errbuf              // error buffer
                            ) ) == NULL)
        {
            fprintf(stderr,"
    Unable to open the adapter. %s is not supported by WinPcap
    ", argv[1]);
            return;
        }
    
    	Arp arp;
    	if(Text1(&arp) == -1)
    	{
    		return ;
    	}
    
    	memcpy(packet, (void*)&arp, ARP_LEN);
        /* Send down the packet */
    	if (pcap_sendpacket(fp, packet, ARP_LEN /* size */) != 0)
    	{
    		fprintf(stderr,"
    Error sending the packet: %s
    ", pcap_geterr(fp));
    		return;
    	}
    
        return;
    }
    








  • 相关阅读:
    Android SHA1与Package获取方式
    《C++语言基础》实践參考——数组作数据成员
    Linux进程间通信——使用共享内存
    Java中ArrayList和LinkedList差别
    hdu1695(莫比乌斯)或欧拉函数+容斥
    Android学习路线(二十四)ActionBar Fragment运用最佳实践
    HTML——使用表格对表单进行布局
    Nginx 訪问日志增长暴增出现尖刀的具体分析
    基于特定值来推断隐藏显示元素的jQuery插件
    怎样配置PHP环境和安装Zendstdio编辑器
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/6846042.html
Copyright © 2011-2022 走看看