Linux内核网桥及接口L2报文追踪:发送帧无法捕获排查
问题排查与解决方案
核心问题分析
你的监听程序无法捕获发送帧,结合ftrace未触发网桥函数的现象,核心问题集中在接口类型错误、原始套接字配置不当和报文发送逻辑缺陷三个方面:
1. 错误使用Dummy接口
你创建的是dummy类型接口而非veth对:
- Dummy接口是独立虚拟接口,仅用于模拟存在性,发送的报文会直接被丢弃,无法传递到网桥或其他接口;
- Veth对是成对的虚拟接口,发送到一端的报文会直接转发到另一端,适合网桥内的跨接口测试。
2. 原始套接字绑定参数错误
监听程序中设置了sll_pkttype = PACKET_OUTGOING:
- 该参数仅捕获从veth0发出的报文,而你需要捕获的是进入veth0的报文;
- 应移除该参数(默认捕获所有类型)或设置为
PACKET_HOST(捕获发往本地接口的报文)。
3. 发送端报文构造不规范
发送代码中用字符串表示MAC地址,但以太网帧要求二进制格式;同时未正确配置发送用的sockaddr_ll结构,导致报文无法正确发送到目标接口。
修正后的环境配置
首先替换Dummy接口为Veth对,确保报文能在网桥内流转:
# 创建网桥并启动 ip link add br0 address 01:02:03:04:00:00 type bridge ip link set br0 up # 创建veth对并设置MAC ip link add veth0 type veth peer name veth1 ip link set veth0 address 01:02:03:04:00:10 ip link set veth1 address 01:02:03:04:00:20 # 将veth接口加入网桥并启动 ip link set veth0 master br0 ip link set veth1 master br0 ip link set veth0 up ip link set veth1 up
修正后的监听程序代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <net/if.h> #include <netpacket/packet.h> #include <net/ethernet.h> int main() { int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock < 0) { perror("socket创建失败"); exit(1); } struct sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = if_nametoindex("veth0"); // 绑定到veth0 if (bind(sock, (struct sockaddr*)&sa, sizeof(sa)) < 0) { perror("bind失败"); close(sock); exit(1); } unsigned char buf[2048]; ssize_t len; printf("开始监听veth0...\n"); while ((len = recv(sock, buf, sizeof(buf), 0)) > 0) { printf("捕获报文长度: %zd字节\n", len); // 可在此添加报文解析逻辑 } close(sock); return 0; }
修正后的发送程序代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <net/if.h> #include <netpacket/packet.h> #include <net/ethernet.h> int main() { int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock < 0) { perror("socket创建失败"); exit(1); } // 构造以太网帧 struct ethhdr { unsigned char h_dest[ETH_ALEN]; unsigned char h_source[ETH_ALEN]; unsigned short h_proto; } __attribute__((packed)); const int PAYLOAD_SIZE = 12; unsigned char frame[sizeof(struct ethhdr) + PAYLOAD_SIZE]; struct ethhdr* eth = (struct ethhdr*)frame; // 设置目标MAC(veth0) unsigned char dest_mac[] = {0x01, 0x02, 0x03, 0x04, 0x00, 0x10}; memcpy(eth->h_dest, dest_mac, ETH_ALEN); // 设置源MAC(veth1) unsigned char src_mac[] = {0x01, 0x02, 0x03, 0x04, 0x00, 0x20}; memcpy(eth->h_source, src_mac, ETH_ALEN); // 设置协议类型(0x88b5) eth->h_proto = htons(0x88b5); // 填充payload memcpy(frame + sizeof(struct ethhdr), "hello world!", PAYLOAD_SIZE); // 配置发送地址结构 struct sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = if_nametoindex("veth1"); // 从veth1发送 sa.sll_halen = ETH_ALEN; memcpy(sa.sll_addr, dest_mac, ETH_ALEN); ssize_t sent = sendto(sock, frame, sizeof(frame), 0, (struct sockaddr*)&sa, sizeof(sa)); if (sent < 0) { perror("sendto失败"); close(sock); exit(1); } printf("发送成功: %zd字节\n", sent); close(sock); return 0; }
L2报文追踪方法
修正环境后,可通过以下方式追踪网桥内的报文流转:
1. 正确使用ftrace追踪网桥函数
网桥核心函数均以br_开头,需调整过滤规则:
cd /sys/kernel/debug/tracing echo br_* > set_ftrace_filter echo function > current_tracer echo 1 > tracing_on ./my_sender echo 0 > tracing_on cat trace
可重点关注br_handle_frame(网桥接收报文入口)、br_forward(转发报文)、br_deliver(交付到本地接口)等函数。
2. 使用tcpdump捕获报文
- 检查发送端是否发出报文:
tcpdump -i veth1 ether host 01:02:03:04:00:10 - 检查网桥是否接收报文:
tcpdump -i br0 ether host 01:02:03:04:00:10 - 检查目标接口是否收到报文:
tcpdump -i veth0 ether host 01:02:03:04:00:10
3. 检查网桥转发表
确认目标MAC已注册到网桥FDB:
bridge fdb show br br0
4. 验证接口状态
确认所有接口已加入网桥并处于UP状态:
ip link show master br0
内容的提问来源于stack exchange,提问作者Abdurrahman Uslu
相关产品推荐
相关产品推荐

