資料鍊路資料的格式由五部分組成:目的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即可看到列印出的鍊路層資訊