以太网的数据结构如图11.10所示,总长度最大为1518字节,最小为64字节,其中目标地址的MAC为6字节,源地址MAC为6字节,协议类型为2字节,含有46~1500字节的数据,尾部为4个字节的CRC校验和。以太网的CRC校验和一般由硬件自动设置或者剥离,应用层不用考虑。
图11-10 以太网帧示意图
在头文件<Linux/if_ether.h>中定义了如下的常量:
#define ETH_ALEN 6 /*以太网地址,即MAC地址,6字节*/
#define ETH_HLEN 14 /*以太网头部的总长度*/
#define ETH_ZLEN 60 /*不含CRC校验的数据最小长度*/
#define ETH_DATA_LEN 1500 /*帧内数据的最大长度*/
#define ETH_FRAME_LEN 1514 /*不含CRC校验和的最大以太网数据长度*/
以太网头部结构定义为如下的形式:
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /*目的以太网地址*/
unsigned char h_source[ETH_ALEN]; /*源以太网地址*/
__be16 h_proto; /*包类型*/
};
套接字文件描述符建立后,就可以从此描述符中读取数据,数据的格式为上述的以太网数据,即以太网帧。套接口建立以后,就可以从中循环读取捕获的链路层以太帧。要建立一个以太网缓冲区可以建立一个大小为ETH_FRAME_LEN的缓冲区,并将以太网的头部指向此缓冲区,例如:
char ef[ETH_FRAME_LEN]; /*以太帧缓冲区*/
struct ethhdr*p_ethhdr; /*以太网头部指针*/
int n;
p_ethhdr = (struct ethhdr*)ef; /*使p_ethhdr指向以太网帧的帧头*/
/*读取以太网数据,n为返回的实际捕获的以太帧的帧长*/
n = read(fd, ef, ETH_FRAME_LEN);
接收数据以后,缓冲区ef与以太网头部的对应关系如图11.11所示。
图11.11 以太网帧缓冲区与以太网头部结构ethhdr的映射关系
因此要获得以太网帧的目的MAC地址、源MAC地址和协议的类型,可以通过p_ethhdr->h_dest、p_ethhdr->h_source和p_ethhdr->h_proto获得。下面的代码将以太网的信息打印出来:
/*打印以太网帧中的MAC地址和协议类型*/
/*目的MAC地址*/
printf("dest MAC: ");
for(i=0; i< ETH_ALEN-1; i++){
printf("%02x-", p_ethhdr->h_dest[i]);
}
printf("%02x/n ", p_ethhdr->h_dest[ETH_ALEN-1]);
/*源MAC地址*/
printf("source MAC: ");
for(i=0; i< ETH_ALEN-1; i++){
printf("%02x-", p_ethhdr->h_source[i]);
}
printf("%02x/n ", p_ethhdr->h_dest[ETH_ALEN-1]);
/*协议类型,0x0800为IP协议, 0x0806为ARP协议, 0x8035为RARP协议*/
printf("protocol: 0x%04x", ntohs(p_ethhdr->h_proto));