Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 協議棧網橋數據處理流程:br_handle_frame 核心邏輯與轉發決策

編輯:Linux 教學

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;
}

四、 關鍵轉發函數

1. 單播轉發 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);  // 驅動發送
}

2. 泛洪轉發 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;
        }
    }
}

五、 FDB (CAM 表) 操作

  • 學習: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,不老化、不學習覆蓋。

六、 Netfilter 掛鉤點

掛鉤點位置典型用途
NF_BR_PRE_ROUTINGbr_handle_frame → br_handle_frame_finishebtables DNAT、橋接防火牆
NF_BR_LOCAL_IN目標為橋自身 / STP BPDU本機處理、STP 協議
NF_BR_FORWARDbr_forward_finish轉發過濾、流量控制
NF_BR_POST_ROUTING發送前SNAT、標記
NF_BR_BROUTINGbr_handle_frame 早期橋接/路由決定
Copyright © Windows教程網 All Rights Reserved