以太网帧跨网口克隆转发异常问题排查求助
问题
我正在尝试编写一个简单的网络桥接模块,编写了如下RX处理函数:
static rx_handler_result_t handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; struct sk_buff *cloned_skb; // Forward packets from eth1 to eth0 if (skb->dev == dev_eth1) { struct ethhdr *eth = eth_hdr(skb); pr_info("Incoming packet: src MAC %pM, dst MAC %pM, ethertype 0x%04x\n", eth->h_source, eth->h_dest, ntohs(eth->h_proto)); cloned_skb = skb_clone(skb, GFP_ATOMIC); if (!cloned_skb) { pr_err("Failed to clone skb\n"); return RX_HANDLER_PASS; } cloned_skb->dev = dev_eth0; //skb_reset_mac_header(cloned_skb); // skb_reset_network_header(cloned_skb); // skb_reset_transport_header(cloned_skb); eth = eth_hdr(cloned_skb); pr_info("Sending packet on eth0: src MAC %pM, dst MAC %pM, ethertype 0x%04x\n", eth->h_source, eth->h_dest, ntohs(eth->h_proto)); dev_queue_xmit(cloned_skb); return RX_HANDLER_CONSUMED; } }
尽管两次打印的帧内容一致,但实际网络中捕获到的帧完全错乱(MAC地址随机且递增,仿佛克隆的skb数据影响了头部)。参考vlan等内核驱动的类似实现方式仍未解决,恳请指点错误原因。
分析与解决
你的问题核心是skb克隆后未正确处理缓冲区的头部偏移与数据所有权,具体原因及修复方式如下:
skb_clone的共享数据隐患
skb_clone()仅克隆skb的元数据(各header指针、设备信息等),但数据缓冲区是与原skb共享的。直接修改克隆skb的dev就调用dev_queue_xmit(),会导致原skb后续被内核其他逻辑修改时,克隆包的数据同步被篡改;同时原skb的头部偏移是基于原设备上下文设置的,切换到新设备后未重置,会让内核在传输过程中错判数据位置,出现乱码MAC地址。头部指针必须重置
你注释掉的skb_reset_mac_header()等函数是关键操作:克隆skb切换到新设备后,必须重置所有头部指针,确保内核处理克隆包时能正确定位各层头部。打印时eth_hdr()能正常获取是因为此时元数据还未被破坏,但传输阶段的校验和计算、驱动头部处理等逻辑会依赖这些指针,未重置必然导致数据偏移错误。正确的修复代码
修改后的代码如下:
static rx_handler_result_t handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; struct sk_buff *cloned_skb; // Forward packets from eth1 to eth0 if (skb->dev == dev_eth1) { struct ethhdr *eth = eth_hdr(skb); pr_info("Incoming packet: src MAC %pM, dst MAC %pM, ethertype 0x%04x\n", eth->h_source, eth->h_dest, ntohs(eth->h_proto)); // 使用skb_copy获取独立数据缓冲区,避免共享数据被篡改 cloned_skb = skb_copy(skb, GFP_ATOMIC); if (!cloned_skb) { pr_err("Failed to copy skb\n"); return RX_HANDLER_PASS; } cloned_skb->dev = dev_eth0; // 重置所有头部指针 skb_reset_mac_header(cloned_skb); skb_reset_network_header(cloned_skb); skb_reset_transport_header(cloned_skb); // 重新设置各层头部偏移,确保内核能正确解析 skb_set_mac_header(cloned_skb, 0); skb_set_network_header(cloned_skb, ETH_HLEN); eth = eth_hdr(cloned_skb); pr_info("Sending packet on eth0: src MAC %pM, dst MAC %pM, ethertype 0x%04x\n", eth->h_source, eth->h_dest, ntohs(eth->h_proto)); dev_queue_xmit(cloned_skb); return RX_HANDLER_CONSUMED; } // 处理非eth1的数据包,返回PASS让内核继续处理 return RX_HANDLER_PASS; }
额外细节注意:原代码未处理skb->dev != dev_eth1的情况,会导致函数无返回值的未定义行为,必须补充返回RX_HANDLER_PASS。
内容的提问来源于stack exchange,提问作者user542088
相关产品推荐
相关产品推荐

