Linux 網橋通過 ioctl(SIOCBRADDIF) 將物理網卡添加為橋端口,內核創建 net_bridge_port 結構、綁定設備、初始化 STP 狀態、更新 FDB、設置混雜模式、掛載 sysfs 節點。本文基於 net/bridge/br_if.c、br_ioctl.c 完整解析添加接口流程。
// brctl addif br0 eth0
ioctl(br_socket_fd, SIOCBRADDIF, &ifr); // ifr.ifr_ifindex = eth0 索引
br_dev_ioctl()
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) {
struct net_bridge *br = netdev_priv(dev);
switch (cmd) {
case SIOCDEVPRIVATE:
return old_dev_ioctl(dev, rq, cmd);
case SIOCBRADDIF:
case SIOCBRDELIF:
return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
default:
return -EOPNOTSUPP;
}
}
add_del_if() → br_add_if()
static int add_del_if(struct net_bridge *br, int ifindex, int isadd) {
if (!capable(CAP_NET_ADMIN)) return -EPERM;
struct net_device *dev = dev_get_by_index(ifindex);
if (!dev) return -EINVAL;
int ret = isadd ? br_add_if(br, dev) : br_del_if(br, dev);
dev_put(dev);
return ret;
}
br_add_if() 詳細流程
int br_add_if(struct net_bridge *br, struct net_device *dev) {
struct net_bridge_port *p;
int err = 0;
// 1. 基本合法性檢查
if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
return -EINVAL; // 非以太網/回環設備
if (dev->hard_start_xmit == br_dev_xmit)
return -ELOOP; // 已經是橋設備,防止環路
if (dev->br_port)
return -EBUSY; // 已屬於其他橋
// 2. 分配並初始化 net_bridge_port
if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))
return PTR_ERR(p);
// 3. 將設備 MAC 作為本地靜態條目插入 FDB
if ((err = br_fdb_insert(br, p, dev->dev_addr, 1))) // is_local=1
goto err_destroy_nbp;
// 4. 創建 sysfs 節點 (/sys/class/net/br0/brif/eth0)
if ((err = br_sysfs_addif(p)))
goto err_del_nbp;
// 5. 設置設備混雜模式(接收所有幀用於學習/轉發)
dev_set_promiscuity(dev, 1);
// 6. 掛載到橋端口鏈表
list_add_rcu(&p->list, &br->port_list);
// 7. STP 狀態機初始化
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br); // 重新計算橋 ID
if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP) && netif_carrier_ok(dev))
br_stp_enable_port(p); // 滿足條件啟用端口
spin_unlock_bh(&br->lock);
// 8. 更新橋 MTU(取所有端口最小值)
dev_set_mtu(br->dev, br_min_mtu(br));
return 0;
err_del_nbp:
del_nbp(p);
err_destroy_nbp:
destroy_nbp(p);
return err;
}
new_nbp()
static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev, unsigned long cost) {
int index = find_portno(br); // 分配端口號 (1-1023)
if (index < 0) return ERR_PTR(index);
struct net_bridge_port *p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p) return ERR_PTR(-ENOMEM);
memset(p, 0, sizeof(*p));
p->br = br;
dev_hold(dev);
p->dev = dev;
p->path_cost = cost; // 基於帶寬計算
p->priority = 0x8000 >> BR_PORT_BITS; // 默認優先級 0x80
dev->br_port = p; // 反向綁定
p->port_no = index;
br_init_port(p); // 初始化 STP 端口變量
p->state = BR_STATE_DISABLED; // 初始狀態
kobject_init(&p->kobj); // sysfs kobject
return p;
}
br_initial_port_cost()
static unsigned long br_initial_port_cost(struct net_device *dev) {
if (dev->speed == SPEED_10) return 100;
if (dev->speed == SPEED_100) return 19;
if (dev->speed == SPEED_1000) return 4;
if (dev->speed == SPEED_2500) return 3;
if (dev->speed == SPEED_10000) return 2;
return 100; // 未知速度默認 10M
}
br_init_port()
port_id、designated_root、designated_bridge、designated_port、designated_cost。forward_delay_timer、hold_timer、message_age_timer。topology_change_ack、config_pending 等。br_fdb_insert(br, p, dev->dev_addr, 1) 將橋端口自身 MAC 標記 is_local=1,查找時直接上送本機協議棧,不再轉發。
/sys/class/net/br0/brif/eth0/
├── priority
├── path_cost
├── state
├── hairpin_mode
├── root_id
├── bridge_id
├── designated_root
├── designated_bridge
├── designated_port
├── designated_cost
├── message_age_timer
├── forward_delay_timer
├── hold_timer
└── ...
br_del_if()br_stp_disable_port()。list_del_rcu()。dev_set_promiscuity(dev, -1)。br_sysfs_delif()。br_fdb_delete_by_port()。net_bridge_port 內存 destroy_nbp()。Windows 與 Linux 動態函式庫深度解析:DLL 與 SO 移植實戰指南
Linux 系統下重新安裝 Linux:GRUB 引導安裝程序啟動的完整操作指南
Linux 桌面無縫存取 Windows「我的文件」:自動掛載與快捷方式建立全攻略
Linux 系統忘記 Root 密碼?5 大經典解鎖方法完整教學
Linux I/O 模型核心概念:同步/非同步與阻塞/非阻塞深度對比
Linux 核心八大核心特徵:整體式架構、動態優先級排程、核心執行緒、跨平台記憶體管理、VFS、模組機制、可擴展系統呼叫、面向物件網路堆疊