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

TCP/IP协议分析–数据链路

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

数据链路数据的格式由五部分组成:目的MAC(6)、本地MAC(6)、数据类型(2)、数据(len)、CRC校验(4)

目的MAC和本地MAC就是收发机器的MAC地址,如果目的MAC的所有bit均为1,则为广播包,局域网内所有机器都会接收这个包,一般用于ARP询问。

数据类型有三种:ARP包(0x0806)、RARP包(0x8035)、IP包(0x0800)

数据:根据数据类型的不同,数据有不同的意义。

对于绝大部分以太网芯片来说,CRC校验是由硬件完成的,所以对于软件来说是不可见的,除非用数据分析仪直接抓网线中的数据。

首先定义链路层的结构体:

//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[1024];   //数据
}ETH_HEADER;

typedef enum
{
    ARP_PACKET=0x0806,
    RARP_PACKET=0x8035,
    IP_PACKET=0x0800
}PACKET_TYPE;

#endif

在linux下,可以通过socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))来申请链路层的套接字,这是一个无链接的套接字。

//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]);
}

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;
}

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");
                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即可看到打印出的链路层信息

抱歉!评论已关闭.