以太网数据帧解码
先了解数据链路层的数据构成(数据链路层会在包头和包尾添加数据,这里仅介绍包头的数据)。数据链路层数据由6位目标MAC地址,6位源MAC地址以及2位的下层协议标识组成。
数据帧头的数据结构如下:
typedef struct EthernetHdr_ {
uint8_t eth_dst[6]; // 目标 MAC 地址
uint8_t eth_src[6]; // 源 MAC 地址
uint16_t eth_type; // 上层协议类型
} __attribute__((__packed__)) EthernetHdr;
获取数据链路层数据
suricata
数据进入decode-ethernet.c
中的DecodeEthernet()
进行数据帧的解码,检查数据包长度后记录数据链路层数据头指针(解码数据包未新开辟空间,而是通过构造相应的结构体,在需要使用相应数据时使用结构体数据偏移取出数据)。
// 解析链路层数据
p->ethh = (EthernetHdr *)pkt;
if (unlikely(p->ethh == NULL))
return TM_ECODE_FAILED;
p为Package
,用于保存数据包相应的数据,但是具体的生命周期不知道是多少。在package
中保存了部分数据。
选择剩余数据的解码方式
在数据链路层数据的最后有下层协议的标识,通过标识我们可以使用对应的函数来对后面数据进行进一步解码。
switch (ntohs(p->ethh->eth_type)) {
case ETHERNET_TYPE_IP:
//printf("DecodeEthernet ip4\n");
DecodeIPV4(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_IPV6:
//printf("DecodeEthernet ip6\n");
DecodeIPV6(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_PPPOE_SESS:
//printf("DecodeEthernet PPPOE Session\n");
DecodePPPOESession(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_PPPOE_DISC:
//printf("DecodeEthernet PPPOE Discovery\n");
DecodePPPOEDiscovery(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_VLAN:
case ETHERNET_TYPE_8021QINQ:
DecodeVLAN(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_MPLS_UNICAST:
case ETHERNET_TYPE_MPLS_MULTICAST:
DecodeMPLS(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
len - ETHERNET_HEADER_LEN, pq);
break;
case ETHERNET_TYPE_DCE:
if (unlikely(len < ETHERNET_DCE_HEADER_LEN)) {
ENGINE_SET_INVALID_EVENT(p, DCE_PKT_TOO_SMALL);
} else {
DecodeEthernet(tv, dtv, p, pkt + ETHERNET_DCE_HEADER_LEN,
len - ETHERNET_DCE_HEADER_LEN, pq);
}
break;
default:
SCLogDebug("p %p pkt %p ether type %04x not supported", p,
pkt, ntohs(p->ethh->eth_type));
}