路由-如何从设备出发(一)——网卡驱动层面

📅 发布时间:2026/7/17 19:48:08
路由-如何从设备出发(一)——网卡驱动层面 设备如何知道第一站是网关Send to gateway发往网关当终端手机试图访问互联网时比如打开浏览器数据包的第一站一定是发给你路由器的 192.168.1.1是不是说我的终端在被DHCP分配ip时就知道了网关ip,然后终端在访问8.8.8.8时他怎么知道先发往网关呢是不是终端又缓存路由表知道第一站是网关1. 终端是如何知道“第一站是网关”的这确实是DHCP的功劳。当你的终端连接Wi-Fi时DHCP协议在分配IP地址比如192.168.1.100的同时会附带一个“选项 3Router Option”把网关IP192.168.1.1告诉终端。终端收到后会把这个信息写入自己的操作系统的路由表中生成一条核心的默认路由Default Route。你可以把这条路由理解为终端网络规则的“最终兜底条款”“如果我要去的目的地不在我直连的局域网网段内就把数据包统统扔给192.168.1.1这个网关去处理。”2. 当终端想访问8.8.8.8时它是如何决策的这个过程在瞬间完成决策逻辑非常清晰步骤如下第一步计算目标是否在“同一网段”终端将自己的IP192.168.1.100和子网掩码通常是255.255.255.0做“与”运算算出自己所在的网段是192.168.1.0/24。然后它用同样的方法算一下目标8.8.8.8属于哪个网段——发现是8.8.8.0/24。第二步查找路由表做出判断终端发现8.8.8.8并不在自己的“本地局域网”192.168.1.0/24里。于是它立刻启用那条“默认路由”规则。第三步封装数据包发往网关既然决定把包发给网关192.168.1.1那么终端在封装这个数据包时会这样操作IP层网络层写清楚“源IP192.168.1.100目标IP8.8.8.8”。MAC层数据链路层写清楚“源MAC你的手机网卡地址目标MAC192.168.1.1的MAC地址”。这里有个非常重要的物理现实在同一个局域网内数据包的传输根本不看IP地址而是看MAC地址。终端正是通过ARP协议地址解析协议提前获取了网关192.168.1.1的MAC地址才能把这个包准确无误地发送给网关的网卡。3. 回答你关于“缓存路由表”的疑问你提到的“缓存路由表”非常准确这里面有两种“缓存”静态的“路由表”这是写在操作系统内核里的在你连接网络的整个期间一直存在。它决定了“目标IP 8.8.8.8 该怎么走”的宏观路径。动态的“ARP缓存表”这是终端为了发送二层数据帧而临时存储的。它记录了“192.168.1.1这个IP对应的MAC地址是多少”。终端会先查这个缓存如果没找到才会在局域网内广播一个ARP请求“谁有192.168.1.1的MAC地址请告诉我。”那我的路由器是接受到802.11 wifi帧是吗这个问题问得非常细致直接触及了Wi-Fi和有线网络在底层的本质区别。简短的回答是严格来说你的路由器接收到的不仅仅是802.11 Wi-Fi帧而是经过“转换”后的数据。这取决于你在路由器的哪个位置去“抓包”或“观察”数据。为了让你彻底明白我们分两层来看1. 物理层天线接收到的确实是802.11帧从纯物理和无线协议的角度讲你的终端手机通过Wi-Fi发送数据数据在空气中是以802.11 Wi-Fi帧的格式传播的。路由器内置的Wi-Fi网卡无线射频芯片的天线接收到的当然是这些无线电波也就是802.11帧。所以如果你拿着一个专业的Wi-Fi嗅探器比如装了Kismet的电脑在路由器旁边抓取空口包你抓到的全是802.11帧头 数据负载。2. 软件层内核驱动在到达你的mongoose之前帧已经被“剥皮”了这是最关键的一点。你的路由器尤其是基于Linux/OpenWrt的系统在Wi-Fi网卡的驱动程序层面做了“帧转换”。当Wi-Fi网卡收到802.11帧后驱动会立即将其转换成标准的以太网帧Ethernet Frame。转换完成后这个数据包才会被递交给Linux内核的网络协议栈TCP/IP栈。也就是说当你的mongoose程序通过Socket接口比如recvfrom读取数据时它看到的已经是纯粹的IP数据包以太网帧格式而不是原始的802.11帧了。为了让你更直观地理解看一个对比观察点数据格式包含的关键信息空气/天线处无线空口802.11 Wi-Fi帧包含源MAC、目的MAC、信号强度RSSI、速率、QoS标签等。路由器CPU/内存mongoose读取时以太网帧 / IP包此时MAC地址仍在但802.11特有的无线控制信息如信道、重传标志已被剥离。对上层应用如HTTP服务器来说它看起来就像是从网线进来的数据包。这对你的开发意味着什么对于你目前的mongoose开发工作这其实是一个好消息你不需要在代码里去解析复杂的802.11帧头比如Beacon、Probe Request等。你的mongoose直接处理的是标准的HTTP请求即GET /或POST /login这些请求携带的就是之前我们讨论的源IP192.168.1.100和目标IP8.8.8.8或192.168.1.1。唯一需要注意的是如果你将来想实现“根据终端Wi-Fi信号强度RSSI做不同认证策略”这种高级功能那你才需要深入到驱动层去获取802.11帧里的无线信息。但如果只是为了做Portal认证弹窗你完全不需要关心802.11帧的细节只管处理你的IP数据包和HTTP协议就可以了。希望这个解释能帮你厘清无线帧和网络协议栈之间的边界如果对驱动转换过程还有兴趣我们可以再深入聊聊Linux内核的bridge或mac80211子系统。驱动会立即将其转换成标准的以太网帧当Wi-Fi网卡收到802.11帧后驱动会立即将其转换成标准的以太网帧Ethernet Frame。为什么要转换为标准的以太网帧标准的以太网帧是什么以太网是什么这个问题问到了计算机网络设计的根基。要回答“为什么要转换”我们需要先搞清楚“以太网”是什么以及它和Wi-Fi的关系。我用一个快递物流的类比来帮你彻底理清这个概念1. 什么是“以太网”Ethernet以太网是一种有线局域网通信标准它规定了数据在网线双绞线、光纤中如何传输。它诞生于1970年代是目前全球最主流、最成熟的有线网络技术。你可以把以太网理解为“有线快递的运输规则”运输工具网线双绞线/光纤包装规格以太网帧Ethernet Frame地址系统MAC地址每个网卡全球唯一的硬件地址所有传统的台式机、服务器、交换机、路由器之间的有线连接都跑的是以太网协议。2. 什么是“标准的以太网帧”以太网帧就是数据在以太网上传输时的“标准快递包裹”格式。它的结构大致如下字段长度作用目的MAC地址6字节接收方网卡的硬件地址源MAC地址6字节发送方网卡的硬件地址类型/长度2字节指示上层协议如IP、ARP数据负载46~1500字节实际要传输的数据如IP包校验和FCS4字节用于检测数据是否损坏这个格式是有线网络世界的通用语言。所有有线网卡、交换机和路由器都能理解和处理这种帧。3. 为什么Wi-Fi帧要转换成以太网帧这就涉及到“兼容性”和“生态统一”的问题了。原因一让上层协议“无感”运行所有的互联网协议TCP/IP、HTTP、DNS等都是基于以太网帧设计的。它们并不关心数据是从网线进来的还是从Wi-Fi天线进来的。如果Wi-Fi不转换成以太网帧那么操作系统的TCP/IP协议栈需要专门为Wi-Fi写一套处理逻辑所有的上层应用浏览器、mongoose都需要区分数据来源整个软件生态会变得极其复杂和割裂转换之后操作系统接收到的始终是以太网帧Wi-Fi和有线网卡对上层来说完全一样。你的mongoose根本不需要知道自己处理的数据是来自Wi-Fi还是网线它只管处理IP包就行了。原因二让网络设备交换机/路由器统一处理路由器的核心功能是转发数据包。如果Wi-Fi帧不转换路由器内部就需要维护两套转发逻辑一套处理有线来的以太网帧一套处理无线来的802.11帧转换之后路由器内部只有一个统一的“以太网转发引擎”无论数据从哪个口进来都转成同一套格式再统一处理。这极大简化了硬件设计和软件实现。原因三保留核心信息剥离无线专属信息802.11帧里包含很多Wi-Fi特有的控制信息比如信号强度RSSI信道编号速率协商信息重传标志QoS优先级这些信息只在无线链路上有意义一旦数据到达路由器它们就完成了使命不再需要传递给上层应用。转换时驱动会剥离这些无线控制头只保留核心的MAC地址 数据负载封装成标准的以太网帧。4. 一个直观的类比场景类比Wi-Fi传输你用无人机Wi-Fi把快递从你家送到小区收发室。无人机的飞行路径、遥控信号、GPS坐标802.11帧头只在空中这段有用。驱动转换收发室的工作人员把无人机卸下来的快递重新打包成一个标准纸箱以太网帧贴上收件人地址MAC地址。路由器内部处理快递站的分拣系统协议栈只认识这种标准纸箱它可以轻松地把纸箱分类、扫描、装上不同的卡车路由转发。mongoose接收你应用层最后收到的是一个已经拆开的标准纸箱里面就是你要的货物HTTP数据。你完全不需要关心它是无人机送来的还是卡车送来的。总结以太网是有线网络的标准以太网帧是它规定的数据格式。Wi-Fi帧是无线网络的数据格式包含很多无线特有的控制信息。转换是为了统一让操作系统、网络协议栈、路由转发引擎和上层应用包括你的mongoose能够用同一套逻辑处理所有数据不管数据是从有线还是无线来的。所以你的mongoose收到的永远是以太网帧里剥出来的IP数据包它不需要知道802.11帧长什么样——这也是操作系统和网络协议栈帮你屏蔽掉的底层细节。图中的netif_input全称netinterface_input就是网卡的输入所以图中192.168.1.100的cliendt通过wifi 802.11帧到达路由器驱动也就是网卡是网卡把802.11帧转换成以太网帧吗图中的netif_input全称netinterface_input就是网卡的输入还是什么驱动你问的这两个点非常精准直接触及了嵌入式网络驱动开发的核心。我来帮你把这层“面纱”彻底揭开。针对你的问题答案是是的网卡硬件固件完成了第一步“拆包”但完全转换成操作系统能识别的以太网帧是由网卡驱动程序配合内核协议栈共同完成的。而图中的netif_input并不是网卡本身而是Linux内核网络协议栈里的一个核心入口函数。netif_input是网卡的输入吗还是什么驱动答netif_input不是网卡也不是驱动。它是 Linux 内核网络协议栈中数据链路层收包处理的总入口函数。在早期的 Linux 内核2.4 及以前中确实有一个名为netif_rx()的函数作为入口。在现代内核3.x 及以后中实际的入口函数是netif_receive_skb()。很多人习惯用netif_input这个名称来泛指“从驱动上来的数据包进入内核网络栈的那个入口点”。所以netif_inputnetif_receive_skb() 内核网络子系统的数据链路层入口它既不是网卡硬件也不是网卡驱动。1.网卡把 802.11 帧转换成以太网帧吗答不是。网卡硬件不做这个转换。网卡硬件只做了以下事情解调无线电波将模拟信号转换为数字信号0/1 比特流。校验 FCS帧校验序列确认数据在无线传输中没有损坏。剥离物理层前导码用于同步时钟的部分。解析出 802.11 帧头包含源 MAC、目的 MAC、QoS 控制字段等。将解析好的原始 802.11 帧数据包含完整的 802.11 MAC 头和负载通过硬件总线PCIe、SDIO、USB 等以中断或 DMA 方式提交给 CPU 和驱动程序。此时数据仍然是 802.11 帧格式还没有变成以太网帧。2、谁把 802.11 帧变成了以太网帧核心答案是 Linux 内核的mac80211子系统中的ieee80211_data_to_8023()函数完成的。这个函数位于Wi-Fi 驱动层软件在数据包被递交到netif_receive_skb()之前就已经执行完毕。完整流程分步详解text【1. 物理层】Wi-Fi 天线捕获无线电波 ↓ 【2. 硬件处理】网卡芯片解调、校验 FCS、剥离物理层前导码 → 输出原始 802.11 帧包含 802.11 MAC 头 数据负载 → 通过 DMA 写入内存触发中断通知 CPU ↓ 【3. 驱动层中断处理】网卡驱动从 DMA 区域读取数据 → 将 802.11 帧数据封装成内核数据结构 sk_buff → 设置 sk_buff-protocol 为 802.11 类型 → 调用 ieee80211_rx()mac80211 子系统的收包入口 ↓ 【4. mac80211 子系统】ieee80211_rx() 对 802.11 帧做各种处理 → 处理 Beacon/Probe Response 等管理帧 → 对于数据帧调用 ieee80211_rx_h_data() ↓ 【5. ★ 转换发生处 ★】ieee80211_rx_h_data() 调用 ieee80211_data_to_8023() → 剥离 802.11 帧头包含源 MAC、目的 MAC、QoS 等 → 读取 802.11 帧头中的 MAC 地址信息 → 重新组装一个标准的以太网帧头目的 MAC 源 MAC Type 字段 → 将组装好的以太网帧头附加到数据负载前面 → 修改 sk_buff-protocol ETH_P_IP或其他上层协议类型 → 修改 sk_buff 的接口类型标志标记为“已是标准以太网帧” ↓ 【6. mac80211 交付】调用 ieee80211_deliver_skb() → 此时 sk_buff 里装的已经是完整的标准以太网帧 → 调用 netif_receive_skb(skb)将 sk_buff 上交给内核网络核心层 ↓ 【7. 网络核心层入口】netif_receive_skb()即图中的 netif_input → 此时输入的是一个完整的标准以太网帧 → 注意此时 sk_buff-data 指向以太网帧头目的 MAC 的第 1 个字节 ↓ 【8. 剥离以太网头】netif_receive_skb() 调用 eth_type_trans() → 读取以太网帧头中的目的 MAC、源 MAC → 读取 Type 字段如 0x0800 IPv4 → 将 sk_buff-data 指针向后移动 14 个字节以太网头长度 → 现在 sk_buff-data 指向 IP 头 → 设置 sk_buff-protocol ETH_P_IP告诉上层这是 IP 包 ↓ 【9. 分发给上层协议】netif_receive_skb() 根据 protocol 分发 → 如果 protocol ETH_P_IP调用 ip_rcv(skb) → 如果 protocol ETH_P_ARP调用 arp_rcv(skb) → 如果 protocol ETH_P_IPV6调用 ipv6_rcv(skb) ↓ 【10. IP 层】ip_rcv()即图中的 ip_input → 此时 sk_buff-data 指向 IP 头已无任何以太网头信息 → 检查 IP 头校验和、版本号、长度 → 处理分片重组 → 调用 ip_route_input() 做路由查找 → 触发 Netfilter PREROUTING 钩子iptables 在此处生效 → 根据路由结果决定本地接收走 ip_local_deliver或转发走 ip_forward完整的以太网帧含14字节以太网头IP数据包以太网头已被剥离data指针指向IP头netif_receive_skb()调用eth_type_trans()完整的数据流修正版现在我们把这两步串起来矛盾就消失了textWi-Fi天线捕获无线电波 ↓ 网卡硬件解调 → 数字信号802.11帧 ↓ 【驱动层】ieee80211_data_to_8023() → 剥离802.11帧头 → 根据802.11帧中的MAC地址组装标准的以太网帧头 → 输出完整的以太网帧目的MAC 源MAC Type IP包 ↓ 【驱动层】调用 netif_rx() 或 napi_gro_receive() → 将完整的以太网帧封装在 sk_buff 中上交给内核 ↓ 【内核网络子系统入口】netif_receive_skb() → 此时 sk_buff-data 指向以太网帧头 → 调用 eth_type_trans(skb, dev) → 读取目的MAC、源MAC、Type字段 → 将 sk_buff-data 向后移动14个字节**剥离以太网头** → 设置 sk_buff-protocol ETH_P_IP → 根据 protocol 调用 ip_rcv() ↓ 【IP层】ip_rcv() → 此时 sk_buff-data 指向IP头已无以太网头