现在的位置: 首页 > 综合 > 正文

TCP/IP协议分析–ARP包

2018年02月11日 ⁄ 综合 ⁄ 共 3839字 ⁄ 字号 评论关闭

当一台主机把以太网数据帧发送到位于同一局域网上的另一台主机时,必须要知道对方的物理地址。如果只知道目的主机的IP地址,就必须通过ARP协议来获取其物理地址。

当链路层数据类型被标记为ARP包时,那么链路层数据段的内容就是ARP包的内容。

首先定义ARP包结构体:

//eth.h  
#ifndef  __ETH__  
#define  __ETH__  
  
typedef unsigned int   u32;  
typedef unsigned short u16;  
typedef unsigned char  u8;

/*链路层结构*/
typedef struct  
{  
    u8 destMAC[6];   //目的MAC  
    u8 sourMAC[6];   //源MAC  
    u16 type;        //类型  
    u8 data[1500];   //数据  
}ETH_HEADER; 
/*链路层数据包类型*/
typedef enum  
{  
    ARP_PACKET=0x0806,      //ARP包  
    RARP_PACKET=0x8035,     //RARP包
    IP_PACKET=0x0800        //IP包
}PACKET_TYPE;

/*ARP包结构*/
typedef struct
{
    u16 hardware;        //硬件地址类型,1表示MAC地址
    u16 protocol;        //协议地址类型,0x0800表示IP地址
    u8    mac_len;         //硬件地址的长度,对于MAC地址为6
    u8    ip_len;          //协议地址长度,对于IP地址来说有4和6,对应IPV4和IPV6
    u16    op;              //操作类型,1表示ARP请求,2表示ARP应答
    u8    sourMAC[6];      //发送者MAC地址
    u8    sourIP[4];       //发送者IP地址
    u8    destMAC[6];      //目的MAC地址,对于请求来说,这里是全0
    u8    destIP[4];       //目的IP地址
}ARP_STRUCT;
/*ARP操作类型*/
typedef enum  
{
    ARP_REQUEST=1,
    ARP_REPLY=2,
    RARP_REQUEST=3,
    RARP_REPLY=4
}ARP_OP;

  
#endif

硬件地址类型(hardware)和协议地址类型(protocol):需要转换的两种协议,一般硬件地址类型是1,协议类型是0x0800,表示MAC和IP地址进行转换。

硬件地址长度(mac_len):对于 MAC地址,长度为6

协议地址长度(ip_len):对于 IP地址,长度为4和6,分别对应IPV4和IPV6.

操作类型(op):1表示ARP请求,2表示ARP应答。还有RARP请求3,RARP应答4.

源MAC地址(sourMAC):发送者硬件地址

源IP地址(sourIP):发送者IP地址

目的MAC地址(destMAC):目的MAC地址,对于ARP请求,该字段全0(因为不知到MAC地址,所以填0),对于ARP应答,应答者会把自己MAC地址填入此处,并回应。

目的IP地址(destIP):目的IP地址,即获取这个IP地址主机的MAC地址

ARP解析,注意网络字节顺序是大端格式,对于大部分系统,需要将其转换为小端格式:

//eth.c
#include<stdio.h>  
#include<netinet/if_ether.h>  

#include"eth.h"  

/*将二进制网卡地址转换为可读的字符地址,与ifconfig命令中的格式相同*/  
void StrMac(u8* str,u8* bin)  
{  
    sprintf(str,"%02X:%02X:%02X:%02X:%02X:%02X",bin[0],bin[1],bin[2],bin[3],bin[4],bin[5]);  
}

/*将二进制IP地址转换为可读的字符地址,与ifconfig命令中的格式相同*/ 
void StrIP(u8* str,u8* bin)
{
    sprintf(str,"%d.%d.%d.%d",bin[0],bin[1],bin[2],bin[3]);
}

/*解析链路层数据,返回值为包类型(大端格式)*/
u16 analyseETH(ETH_HEADER* eth)  
{  
    u8 mac[20];  
    StrMac(mac,eth->destMAC);  
    printf("destMAC:%s\n",mac);  
    StrMac(mac,eth->sourMAC);  
    printf("sourMAC:%s\n",mac);  
    printf("ETH_type:%04x\n",htons(eth->type)); //htons返回小端格式的数据  
    return eth->type;  
}

void analyseARP(ARP_STRUCT* arp)
{
    static int num = 0;
    u8 mac_ip[20];
    printf("---------ARP STRUCT[%d]---------\n",num++);
    printf("hardware:%04x\n",htons(arp->hardware));
    printf("protocol:%04x\n",htons(arp->protocol));

    printf("mac_len:%d\n",arp->mac_len);
    printf("ip_len:%d\n",arp->ip_len);
    printf("op:%04x\n",htons(arp->op));
    StrMac(mac_ip,arp->sourMAC);
    printf("sour_mac:%s\n",mac_ip);
    StrIP(mac_ip,arp->sourIP);
    printf("sour_IP:%s\n",mac_ip);
    StrMac(mac_ip,arp->destMAC);
    printf("dest_mac:%s\n",mac_ip);
    StrIP(mac_ip,arp->destIP);
    printf("dest_IP:%s\n",mac_ip);
}

int main()
{
    int socket_fd;
    ETH_HEADER* eth;  
    u32 num = 0;  
    u16 type;
    u8  buf[10240];
    ssize_t len;
    socket_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); //申请链路层的套接字
    if(socket_fd == -1)
    {  
        printf("socket error!\n");  
        return -1;  
    }  
    while(1)  
    {  
        len = recv(socket_fd,buf,sizeof(buf),0);  
        if(len == -1)  
        {
            printf("recv error!\n");  
            break;  
        }
        else if(len == 0)
        {
            continue;
        }
        printf("*********ETH HEADER[%d]*********\n",num++);
        printf("recv_len:%d\n",len);
        eth = (ETH_HEADER*)buf;
        type = analyseETH(eth);
        switch(htons(type))
        {
            case ARP_PACKET:
                printf("ARP_PACKET!\n");
                analyseARP((ARP_STRUCT*)eth->data);
                break;
            case RARP_PACKET:
                printf("RARP_PACKET!\n");
                break;
            case IP_PACKET:
                printf("IP_PACKET!\n");
                break;
            default:
                printf("what?\n");
                break;
        }
    }
    close(socket_fd);
    return 0;  
}  
 

执行gcc eth.c -o eth编译

./eth 运行,就可以看到ARP包的信息。

因为ARP应答时,链路层的目的MAC地址已经被替换为ARP请求者的MAC地址,所以只能接受本机发出的和ARP应答和回应给本机的ARP应答包。(除非将网卡配置为混杂模式)

抱歉!评论已关闭.