在 Linux 核心網路子系統中,網橋作為一種虛擬的第二層交換設備,其創建流程涉及從用戶空間到核心空間的多層交互。本文將深入剖析 br_add_bridge、new_bridge_dev 以及 br_dev_setup 等關鍵函數,揭示網橋 net_device 結構的初始化細節。
當用戶執行 brctl addbr br0 時,工具程序會調用 ioctl(br_socket_fd, SIOCBRADDBR, brname),進入核心空間的 br_ioctl_deviceless_stub 函數。該函數根據命令類型分發處理邏輯:
int br_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
{
switch (cmd) {
case SIOCBRADDBR: // 新建網橋
case SIOCBRDELBR: // 刪除網橋
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(buf, uarg, IFNAMSIZ))
return -EFAULT;
buf[IFNAMSIZ-1] = '\0';
return (cmd == SIOCBRADDBR) ? br_add_bridge(buf) : br_del_bridge(buf);
}
return -EOPNOTSUPP;
}
此處會檢查 CAP_NET_ADMIN 權限,並通過 copy_from_user 將網橋名稱從用戶空間拷貝到核心緩衝區,隨後調用 br_add_bridge 正式開始創建流程。
br_add_bridge 負責分配並註冊網橋對應的 net_device:
int br_add_bridge(const char *name)
{
struct net_device *dev = new_bridge_dev(name);
if (!dev) return -ENOMEM;
rtnl_lock();
if (strchr(dev->name, '%')) {
int ret = dev_alloc_name(dev, dev->name);
if (ret < 0) goto err1;
}
int ret = register_netdevice(dev);
if (ret) goto err2;
dev_hold(dev);
rtnl_unlock();
ret = br_sysfs_addbr(dev);
dev_put(dev);
if (ret) unregister_netdev(dev);
return ret;
err2: free_netdev(dev);
err1: rtnl_unlock(); return ret;
}
關鍵步驟包括:
new_bridge_dev 分配並初始化 net_device 及其私有數據 net_bridge% 則由核心自動分配名稱(如 br0)register_netdevice 向核心註冊網絡設備br_sysfs_addbr 在 sysfs 中創建相關屬性節點此函數分配 net_device 並初始化網橋私有結構 net_bridge:
static struct net_device *new_bridge_dev(const char *name)
{
struct net_device *dev = alloc_netdev(sizeof(struct net_bridge), name, br_dev_setup);
if (!dev) return NULL;
struct net_bridge *br = netdev_priv(dev);
br->dev = dev;
br->lock = SPIN_LOCK_UNLOCKED;
INIT_LIST_HEAD(&br->port_list); // 端口列表初始化
br->hash_lock = SPIN_LOCK_UNLOCKED;
// STP 協議相關初始化(略)
br->bridge_id.prio[0] = 0x80;
br->stp_enabled = 0;
br->ageing_time = 300 * HZ;
INIT_LIST_HEAD(&br->age_list);
br_stp_timer_init(br);
return dev;
}
此處完成了自旋鎖初始化、端口列表(port_list)與老化列表(age_list)的鏈表頭初始化,以及 STP 定時器的設置。
br_dev_setup 為 net_device 綁定核心操作函數指針:
void br_dev_setup(struct net_device *dev)
{
memset(dev->dev_addr, 0, ETH_ALEN);
ether_setup(dev); // 以太網基礎設置
dev->do_ioctl = br_dev_ioctl;
dev->get_stats = br_dev_get_stats;
dev->hard_start_xmit = br_dev_xmit; // 數據發送入口
dev->open = br_dev_open;
dev->set_multicast_list = br_dev_set_multicast_list;
dev->change_mtu = br_change_mtu;
dev->destructor = free_netdev;
dev->stop = br_dev_stop;
dev->accept_fastpath = br_dev_accept_fastpath;
dev->tx_queue_len = 0;
dev->set_mac_address = NULL;
dev->priv_flags = IFF_EBRIDGE; // 標識為橋設備
}
值得注意的是,橋設備的 hard_start_xmit 指向 br_dev_xmit,這是數據包轉發的核心入口;priv_flags 設置為 IFF_EBRIDGE 以區分於普通網卡。
網橋的創建過程本質上是一個虛擬 net_device 的註冊過程,核心在於私有結構 net_bridge 的精心初始化:端口管理列表、MAC 地址學習雜湊表、STP 狀態機以及定時器機制。理解這一流程,有助於深入掌握 Linux 虛擬網絡設備的設計哲學與實現細節。