一、802.11帧与802.3帧的根本区别
1.1 802.11帧结构(无线帧)
+----------------+----------------+----------------+----------------+
| Frame Control | Duration/ID | Address 1 (DA) | Address 2 (SA) |
| (2字节) | (2字节) | (6字节) | (6字节) |
+----------------+----------------+----------------+----------------+
| Address 3 (BSS)| Sequence Ctrl | Address 4 | QoS Control |
| (6字节) | (2字节) | (6字节,可选) | (2字节,可选) |
+----------------+----------------+----------------+----------------+
| Payload (数据) | FCS (校验) |
| (可变长度) | (4字节) |
+----------------+----------------+
特点:
- 4个地址字段:Address 1(DA), Address 2(SA), Address 3(BSSID), Address 4(可选)
- Frame Control:包含协议版本、类型、子类型、标志位等
- Duration/ID:用于无线介质的NAV(网络分配向量)机制
- Sequence Control:用于分片和重排序
- QoS Control:802.11e QoS支持
- FCS:32位CRC校验
1.2 802.3帧结构(以太网帧)
+----------------+----------------+----------------+----------------+
| Destination MAC| Source MAC | EtherType/Length| Payload |
| (6字节) | (6字节) | (2字节) | (46-1500字节) |
+----------------+----------------+----------------+----------------+
| FCS (校验) |
| (4字节) |
+----------------+
特点:
- 2个地址字段:只有DA和SA
- EtherType/Length:标识上层协议(如0x0800=IPv4, 0x86DD=IPv6)
- 无无线特有的控制字段
二、为什么需要转换?
2.1 Linux网络栈的期望
Linux网络栈只理解802.3帧,原因如下:
-
网络层协议基于802.3设计:
c// Linux网络栈期望的帧格式 struct ethhdr { unsigned char h_dest[ETH_ALEN]; // 目的MAC unsigned char h_source[ETH_ALEN]; // 源MAC __be16 h_proto; // EtherType }; -
IP协议栈直接访问以太网头部:
c// IP层代码期望从以太网头部后开始 struct iphdr *iph = (struct iphdr *)(skb->data + ETH_HLEN); -
socket层基于802.3:
c// 应用层通过socket接收的数据已经是802.3格式 recv(sockfd, buf, len, 0);
2.2 实际应用场景
场景1:STA接收AP转发的数据
[服务器] → [有线网络] → [AP] → [无线网络] → [STA]
802.3 802.3 802.11 需要转换回802.3
- 服务器发送802.3帧到AP
- AP将802.3帧封装成802.11帧发送给STA
- STA接收后必须转换回802.3帧,才能让上层应用理解
场景2:AP接收STA的数据
[STA] → [无线网络] → [AP] → [有线网络] → [服务器]
802.11 802.11 需要转换 802.3
- STA发送802.11帧到AP
- AP必须转换为802.3帧才能在有线网络上传输
场景3:无线桥接模式
[STA1] → [AP/桥] → [STA2]
802.11 转换 802.11
- 即使两端都是无线,桥接设备也需要先转换为802.3再转发
三、转换的具体内容
3.1 802.11 → 802.3转换过程
在MT7916驱动中的实现:
c
// cmm_data.c: indicate_802_11_pkt()
VOID indicate_802_11_pkt(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk, UCHAR wdev_idx)
{
UCHAR Header802_3[LENGTH_802_3];
UCHAR *pData = pRxBlk->pData;
INT data_len = pRxBlk->DataSize;
// 1. 构建802.3头部
NdisMoveMemory(Header802_3, pRxBlk->Addr1, MAC_ADDR_LEN); // DA
NdisMoveMemory(Header802_3 + MAC_ADDR_LEN, pRxBlk->Addr2, MAC_ADDR_LEN); // SA
// 2. 判断是LLC/SNAP封装还是原始802.3
if (is_amsdu_packet(pData, data_len)) {
// AMSDU包,特殊处理
} else if (is_eapol_packet(pData)) {
// EAPOL认证包
Header802_3[12] = 0x88;
Header802_3[13] = 0x8E;
} else {
// 普通数据,从LLC/SNAP头部提取EtherType
Header802_3[12] = pData[6]; // EtherType高字节
Header802_3[13] = pData[7]; // EtherType低字节
pData += 8; // 跳过LLC/SNAP头部(3字节LLC + 5字节SNAP)
data_len -= 8;
}
// 3. 将802.3头部添加到包前面
NdisMoveMemory(skb_push(skb, LENGTH_802_3), Header802_3, LENGTH_802_3);
// 4. 去除802.11头部和FCS
skb_pull(skb, pRxBlk->HwHdrLen); // 去除802.11头部
skb_trim(skb, data_len); // 去除FCS
}
转换步骤:
-
提取地址:
- DA ← Address 1
- SA ← Address 2
-
提取EtherType:
- 从LLC/SNAP头部提取(偏移6-7字节)
- 常见值:0x0800(IPv4), 0x86DD(IPv6), 0x0806(ARP)
-
去除802.11特有字段:
- Frame Control
- Duration/ID
- Address 3 (BSSID)
- Address 4 (可选)
- Sequence Control
- QoS Control
- FCS
-
添加802.3头部:
- DA (6字节)
- SA (6字节)
- EtherType (2字节)
3.2 硬件转换 vs 软件转换
MT7916支持硬件转换:
c
// cmm_data.c: indicate_802_3_pkt()
VOID indicate_802_3_pkt(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk, UCHAR wdev_idx)
{
// 硬件已经完成了802.11→802.3转换
// RX_BLK中pRxBlk->pData直接指向802.3帧
if (check_rx_pkt_pn_allowed(pAd, pRxBlk) == FALSE) {
// PN检查失败,丢弃
RELEASE_NDIS_PACKET(pAd, pRxBlk->pRxPacket, NDIS_STATUS_FAILURE);
return;
}
// 直接上报,无需转换
announce_or_forward_802_3_pkt(pAd, pRxBlk->pRxPacket, ...);
}
硬件转换的优势:
- 性能更高:硬件并行处理,不占用CPU
- 延迟更低:减少软件处理步骤
- 代码更简单:驱动无需处理复杂的转换逻辑
软件转换的场景:
- 硬件不支持转换
- 特殊包类型(如EAPOL)需要特殊处理
- 调试和监控模式
四、为什么不能直接使用802.11帧?
4.1 协议栈不兼容
c
// Linux IP层代码示例
// net/ipv4/ip_input.c
int ip_rcv(struct sk_buff *skb, struct net_device *dev, ...)
{
struct iphdr *iph;
// 假设skb->data指向IP头部
// 如果传入的是802.11帧,这里会解析错误!
if (skb->len < sizeof(struct iphdr))
goto drop;
iph = ip_hdr(skb); // 直接访问IP头部
if (iph->ihl < 5 || iph->version != 4)
goto drop;
// ... 继续处理IP包
}
问题:
- 如果传入802.11帧,
ip_hdr(skb)会指向Frame Control字段 - IP版本检查会失败(Frame Control不是0x4)
- 整个协议栈无法工作
4.2 应用程序不兼容
c
// 应用层代码
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, ...);
send(sockfd, "Hello", 5, 0);
// 接收数据
char buf[1024];
recv(sockfd, buf, sizeof(buf), 0);
// 应用层期望收到的是纯数据,不是802.11帧
// 如果收到802.11帧,应用层需要自己解析Frame Control、Address字段等
// 这违背了网络分层的设计原则
4.3 网络设备不兼容
[无线STA] → [AP] → [交换机] → [路由器] → [互联网]
802.11 转换 802.3 802.3 802.3
- 交换机:基于802.3 MAC地址转发,不理解802.11
- 路由器:基于802.3接口接收数据
- 防火墙:基于802.3头部进行过滤
- 负载均衡器:基于802.3+IP+TCP头部进行分发
五、特殊情况处理
5.1 EAPOL帧(802.1X认证)
c
// EAPOL帧需要特殊处理,因为EtherType是0x888E
if (is_eapol_packet(pData)) {
Header802_3[12] = 0x88;
Header802_3[13] = 0x8E;
// EAPOL帧通常不需要LLC/SNAP封装
// 直接从802.11 Payload开始就是EAPOL数据
}
5.2 AMSDU聚合帧
c
// AMSDU包含多个802.3子帧
VOID indicate_amsdu_pkt(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk, UCHAR wdev_idx)
{
UCHAR *pData = pRxBlk->pData;
INT DataSize = pRxBlk->DataSize;
while (DataSize > 14) { // 至少要有802.3头部
// 每个子帧格式:DA(6) + SA(6) + Length(2) + Payload + Padding
INT SubFrameSize = (pData[12] << 8) + pData[13] + 14;
// 提取单个子帧
CloneAndAnnounce(pAd, pData, SubFrameSize);
pData += SubFrameSize;
DataSize -= SubFrameSize;
}
}
5.3 监控模式(Monitor Mode)
c
// 监控模式下,不转换,直接上报原始802.11帧
#ifdef SNIFFER_RADIOTAP_SUPPORT
if (MONITOR_ON(pAd)) {
if (asic_trans_rxd_into_radiotap(pAd, rx_packet, rx_blk) == NDIS_STATUS_SUCCESS)
announce_802_11_radiotap_packet(pAd, rx_packet, rx_blk);
// 不转换,直接上报给tcpdump/wireshark
}
#endif
用途:
- 无线抓包分析
- 入侵检测
- 网络诊断
六、总结
| 方面 | 802.11帧 | 802.3帧 | 为什么需要转换 |
|---|---|---|---|
| 地址字段 | 4个地址 | 2个地址 | Linux网络栈只理解2个地址 |
| 控制字段 | Frame Control, Duration, Sequence等 | 无 | 上层协议不需要这些无线特有字段 |
| 校验 | 32位FCS | 32位FCS | 相同,但位置不同 |
| 协议标识 | LLC/SNAP头部中的EtherType | EtherType字段 | 位置不同,需要提取 |
| 网络栈期望 | 不支持 | 完全支持 | Linux协议栈基于802.3设计 |
| 应用层接口 | 不兼容 | 兼容 | socket API期望802.3格式 |
| 网络设备 | 无线设备专用 | 通用 | 交换机、路由器只理解802.3 |
核心原因:
- Linux网络栈基于802.3设计,所有协议层都假设底层是802.3
- 应用程序通过socket API通信,期望收到的是纯数据,不是802.11帧
- 网络设备(交换机、路由器)只理解802.3,无法处理802.11帧
- 网络分层原则:上层不应该关心下层的具体实现细节
因此,802.11→802.3转换是无线驱动的必要功能,MT7916通过硬件或软件方式实现这一转换,确保无线接收的数据能够被Linux网络栈正确处理。