Linux 網橋在數據鏈路層實現二層轉發,核心處理函數 br_handle_frame() 接收來自端口的數據幀,經過學習、STP、目標查找、泛洪/轉發/上送本機等決策。本文基於內核源碼(net/bridge/br_input.c)詳細解析接收處理流程、CAM 表查找、轉發路徑、混雜模式處理。
// 設備初始化時註冊
br_dev_setup() → dev->rx_handler = br_handle_frame;
// 或早期版本
br_handle_frame_hook = br_handle_frame;
br_handle_frame()
int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) {
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
// 1. 端口狀態檢查
if (p->state == BR_STATE_DISABLED)
goto err;
// 2. 源 MAC 非法檢查(組播/廣播源地址非法)
if (eth_hdr(skb)->h_source[0] & 1)
goto err;
// 3. MAC 地址學習(LEARNING/FORWARDING 狀態)
if (p->state == BR_STATE_LEARNING || p->state == BR_STATE_FORWARDING)
br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0); // 動態條目
// 4. STP 協議處理(目標 MAC 為 Bridge Group Address 01:80:c2:00:00:00)
if (p->br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) {
if (!dest[5]) { // 01:80:c2:00:00:00 標準 STP BPDU
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, NULL, br_stp_handle_bpdu);
return 1;
}
}
// 5. 僅 FORWARDING 狀態進行轉發決策
else if (p->state == BR_STATE_FORWARDING) {
// br_should_route_hook 通常為空(不路由)
if (br_should_route_hook) { ... }
// 6. 目標 MAC 是橋設備自身 → 上送協議棧
if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))
skb->pkt_type = PACKET_HOST;
// 7. Netfilter PRE_ROUTING 掛鉤
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish);
return 1;
}
err:
kfree_skb(skb);
return 1;
}
br_handle_frame_finish()
int br_handle_frame_finish(struct sk_buff *skb) {
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = skb->dev->br_port;
struct net_bridge *br = p->br;
struct net_bridge_fdb_entry *dst;
int passedup = 0;
// 1. 混雜模式:克隆一份上送 AF_PACKET
if (br->dev->flags & IFF_PROMISC) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
// 2. 組播/廣播:泛洪 + 上送本機
if (dest[0] & 1) {
br_flood_forward(br, skb, !passedup); // 轉發給其他端口
if (!passedup)
br_pass_frame_up(br, skb); // 本機協議棧處理
goto out;
}
// 3. 單播:查找 FDB (CAM 表)
dst = __br_fdb_get(br, dest);
// 4. 目標是本機端口
if (dst && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
goto out;
}
// 5. 找到目標端口:單播轉發
if (dst) {
br_forward(dst->dst, skb);
goto out;
}
// 6. 未知單播:泛洪
br_flood_forward(br, skb, 0);
out:
return 0;
}
br_forward()
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) {
struct net_bridge *br = to->br;
if (to->state != BR_STATE_FORWARDING) return;
// 避免回送到接收端口
if (skb->dev == to->dev) return;
// 克隆或直接轉發
if (br->dev->flags & IFF_PROMISC) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) __br_forward(to, skb2);
}
__br_forward(to, skb);
}
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) {
skb->dev = to->dev;
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, skb->dev, NULL, br_forward_finish);
}
int br_forward_finish(struct sk_buff *skb) {
return dev_queue_xmit(skb); // 驅動發送
}
br_flood_forward()
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone) {
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_FORWARDING) continue;
if (p->dev == skb->dev) continue; // 不發回源端口
if (clone) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) continue;
__br_forward(p, skb2);
} else {
__br_forward(p, skb);
skb = NULL; // 原 skb 已轉移,後續端口需克隆
clone = 1;
}
}
}
br_fdb_insert(br, port, src_mac, 0) 創建動態條目,老化時間 br->ageing_time (默認 300s)。__br_fdb_get(br, dest_mac) 哈希查找,返回 net_bridge_fdb_entry 包含目標端口指針 dst 與 is_local 標志。bridge fdb add 設置 is_static=1,不老化、不學習覆蓋。| 掛鉤點 | 位置 | 典型用途 |
|---|---|---|
| NF_BR_PRE_ROUTING | br_handle_frame → br_handle_frame_finish | ebtables DNAT、橋接防火牆 |
| NF_BR_LOCAL_IN | 目標為橋自身 / STP BPDU | 本機處理、STP 協議 |
| NF_BR_FORWARD | br_forward_finish | 轉發過濾、流量控制 |
| NF_BR_POST_ROUTING | 發送前 | SNAT、標記 |
| NF_BR_BROUTING | br_handle_frame 早期 | 橋接/路由決定 |