網絡子係統30_橋接子係統通用接口
// 添加網橋設備
// 參數;
// name,需要全局唯一
// 調用路徑:socket ioctl->br_add_bridge
// 函數主要任務:
// 1.創建一個新的網絡設備
// 2.初始化網絡設備的通用字段以及網橋設備的字段
// 3.向係統注冊網絡設備
1.1 int br_add_bridge(const char *name)
{
struct net_device *dev;//net_bridge->dev
int ret;
dev = new_bridge_dev(name);//創建一個新的網橋設備
if (!dev)
return -ENOMEM;
rtnl_lock();//獲取rtnl鎖
if (strchr(dev->name, '%')) {//名字中提供了通配符,通過係統遞增網橋名中的%d
ret = dev_alloc_name(dev, dev->name);//由係統分配設備名
if (ret < 0)
goto err1;
}
ret = register_netdevice(dev);//注冊網絡設備設備
if (ret)
goto err2;
dev_hold(dev);//遞增設備引用計數
rtnl_unlock();//由rtnl_unlock完成register_netdevice的下半部操作
ret = br_sysfs_addbr(dev);//初始化網橋相關的sysfs
dev_put(dev);
if (ret)
unregister_netdev(dev);
out:
return ret;
err2:
free_netdev(dev);
err1:
rtnl_unlock();
goto out;
}
// 分配網橋設備
// 網橋設備使用網絡設備的通用控製塊net_device
// 調用路徑:br_add_bridge->new_bridge_dev
// 函數主要任務:
// 1.分配網絡設備描述符
// 2.特定於網橋設備的初始化函數初始化設備描述符
// 3.初始化網橋id = [0x80,0x00,0x00 0x00 0x00 0x00 0x00 0x00],其中6字節的以太網地址部分,添加網橋端口時更新。
// 4.初始化網橋路徑開銷,拓撲變化,定時器等字段
// 5.初始化網橋定時器
1.2 static struct net_device *new_bridge_dev(const char *name)
{
struct net_bridge *br;
struct net_device *dev;
dev = alloc_netdev(sizeof(struct net_bridge), name,
br_dev_setup);//分配一個net_device,提供初始化函數
if (!dev)
return NULL;
br = netdev_priv(dev);//dev->priv為一個net_bridge結構
br->dev = dev;//回指指針
spin_lock_init(&br->lock);//初始化網橋的鎖
INIT_LIST_HEAD(&br->port_list);//網橋的端口列表
spin_lock_init(&br->hash_lock);//轉發數據庫的表
br->bridge_id.prio[0] = 0x80;//網橋id中第一部分優先級
br->bridge_id.prio[1] = 0x00;
memset(br->bridge_id.addr, 0, ETH_ALEN);//網橋id中第二部分mac地址
br->stp_enabled = 0;//stp不使能
br->designated_root = br->bridge_id;///初始化時,根網橋id為自己的id
br->root_path_cost = 0;//到根網橋的最佳路徑開銷為0,因為初始化時,認為自己就是根網橋
br->root_port = 0;//根端口的端口號
br->bridge_max_age = br->max_age = 20 * HZ;//BPDU信息的生存期
br->bridge_hello_time = br->hello_time = 2 * HZ;//定期產生配置BPDU的時間間隔
br->bridge_forward_delay = br->forward_delay = 15 * HZ;//狀態轉移定時器
br->topology_change = 0;//指示根網橋在配置BPDU中設定一個特殊標識TC,將拓撲變化通知其他網橋
br->topology_change_detected = 0;//當探測到拓撲變化時,就會設定該標誌
br->ageing_time = 300 * HZ;//轉發項最長沒有被使用的時間
INIT_LIST_HEAD(&br->age_list);//轉發項的最近最少被使用鏈表
br_stp_timer_init(br);//初始化網橋與端口使用的定時器
return dev;
}
// 網橋net_device初始化函數
// 參數:
// dev, 網橋設備的設備描述符
// 調用路徑: br_add_bridge->new_bridge_dev->alloc_netdev->br_dev_setup
// 函數主要任務:
// 1.以太網通用例程初始化dev結構
// 2.初始化dev的函數指針為網橋設備專用指針
// 注:linux網橋是以太網橋,以虛擬網絡設備存在於內核中
1.3 void br_dev_setup(struct net_device *dev)
{
memset(dev->dev_addr, 0, ETH_ALEN);//設備mac地址為0
ether_setup(dev);//調用以太網設備的初始化例程
dev->do_ioctl = br_dev_ioctl;//網橋設備特殊文件的ioctl命令
dev->get_stats = br_dev_get_stats;//統計信息
dev->hard_start_xmit = br_dev_xmit;//傳輸函數
dev->open = br_dev_open;//設備開啟函數,在dev_open中被調用
dev->set_multicast_list = br_dev_set_multicast_list;//設置多播列表
dev->change_mtu = br_change_mtu;//mtu改變
dev->destructor = free_netdev;//在dev_destory中被調用
SET_MODULE_OWNER(dev);
dev->stop = br_dev_stop;//在dev_close中被調用
dev->accept_fastpath = br_dev_accept_fastpath;
dev->tx_queue_len = 0;//不使用隊列規則
dev->set_mac_address = NULL;
dev->priv_flags = IFF_EBRIDGE;//私有字段,通用框架不會使用此字段,由網橋設備指示此設備為網橋設備
}
// 刪除網橋設備
// 在刪除網橋前,需要先關閉網橋
// 調用路徑:socket ioctl->br_del_bridge
// 函數主要任務:
// 1.在rtnl鎖的保護下,執行刪除操作
// 2.獲取設備描述符
// 3.檢查是否為網橋設備
// 2.1 dev->priv_flags=IFF_EBRIDGE
// 4.刪除網橋
2.1 int br_del_bridge(const char *name)
{
struct net_device *dev;
int ret = 0;
rtnl_lock();//獲取rtnl鎖
dev = __dev_get_by_name(name);//通過設備名hash表中查找網橋設備
if (dev == NULL)
ret = -ENXIO;
else if (!(dev->priv_flags & IFF_EBRIDGE)) {//試圖去釋放一個非網橋設備
ret = -EPERM;
}
else if (dev->flags & IFF_UP) {//刪除設備前要先關閉設備
ret = -EBUSY;
}
else
del_br(netdev_priv(dev));//執行實際的刪除工作
rtnl_unlock();//在解鎖時,完成注銷設備的下半部操作
return ret;
}
// 刪除網橋設備
// 調用路徑:br_del_bridge->del_br
// 函數主要任務:
// 1.依次刪除網橋端口
// 2.停用網橋的垃圾回收定時器
// 3.注銷網絡設備
2.2 static void del_br(struct net_bridge *br)
{
struct net_bridge_port *p, *n;
//通過xx_safe遍曆鏈表,可以邊遍曆邊刪除
list_for_each_entry_safe(p, n, &br->port_list, list) {
br_sysfs_removeif(p);//在sysfs中刪除網橋端口
del_nbp(p);//刪除網橋端口
}
del_timer_sync(&br->gc_timer);//停用垃圾回收機製
br_sysfs_delbr(br->dev);//在sysfs中刪除網橋設備
unregister_netdevice(br->dev);//注銷設備
}
// 刪除網橋端口
// 函數主要任務:
// 1.恢複端口的非混雜模式
// 2.關閉端口stp功能
// 3.刪除端口在轉發數據中的信息
// 4.刪除端口使用的定時器
// 注:linux網橋端口需要運行在混雜模式,這樣便可以接收共享介質上所有的數據幀。
2.3 static void del_nbp(struct net_bridge_port *p)
{
struct net_bridge *br = p->br;
struct net_device *dev = p->dev;
//混雜模式通過計數器的方式,而不是二值的形式記錄當前狀態
dev_set_promiscuity(dev, -1);//網橋的端口均處於混雜模式,遞減混雜模式計數器
spin_lock_bh(&br->lock);
br_stp_disable_port(p);//設置端口為指定端口,刪除端口相關的三個定時器,更新網橋的配置信息,必要時發送配置BPDU
spin_unlock_bh(&br->lock);
br_fdb_delete_by_port(br, p);//刪除轉發數據庫中關於端口的信息
list_del_rcu(&p->list);
del_timer_sync(&p->message_age_timer);//刪除端口使用的三個定時器,這三個定時器在br_stp_disable_port中已經被刪除
del_timer_sync(&p->forward_delay_timer);
del_timer_sync(&p->hold_timer);//BPDU的傳輸速率限製
call_rcu(&p->rcu, destroy_nbp_rcu);//釋放net_bridge_port內存
}
// 添加刪除網橋端口
// 參數:
// ifindex,以太網設備索引
// isadd,指示添加或刪除操作
// 調用路徑:網橋特殊設備文件ioctl->add_del_if
3.1 static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
{
struct net_device *dev;
int ret;
//admin權限
if (!capable(CAP_NET_ADMIN))
return -EPERM;
//通過index獲取設備
dev = dev_get_by_index(ifindex);
if (dev == NULL)
return -EINVAL;
//添加刪除端口
if (isadd)
ret = br_add_if(br, dev);
else
ret = br_del_if(br, dev);
//遞減端口引用計數,因為在dev_get_by_index中會遞增設備的引用計數
dev_put(dev);
return ret;
}
// 添加網橋端口
// 調用路徑:add_del_if->br_add_if
// 函數主要任務:
// 1.檢查設備描述符合法性
// 1.1 回環設備,非以太網設備,均不能作為網橋端口
// 1.2 網橋設備不能作為網橋端口
// 1.3 已經作為網橋端口的設備,不能重複添加
// 2. 分配網橋端口描述符
// 3. 將該端口信息添加到轉發數據庫
// 4. 設置端口混雜模式
// 5. 重新計算網橋id
// 6. 如果端口處於開啟模式,開啟端口stp
// 注:網橋添加刪除端口,會導致重新計算網橋id
3.2 int br_add_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
int err = 0;
//網橋端口隻能是以太網設備,不能為回環設備
if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
return -EINVAL;
//網橋設備的hard_start_xmit為br_dev_xmit
if (dev->hard_start_xmit == br_dev_xmit)
return -ELOOP;
//已經為某個網橋的端口
if (dev->br_port != NULL)
return -EBUSY;
//分配一個網橋端口控製塊
if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))
return PTR_ERR(p);
//將端口地址添加到轉發數據庫
if ((err = br_fdb_insert(br, p, dev->dev_addr, 1)))
destroy_nbp(p);
//在sysyfs中添加信息
else if ((err = br_sysfs_addif(p)))
del_nbp(p);
else {
dev_set_promiscuity(dev, 1);//設置端口為混雜模式
//將端口添加到網橋的port_list中
list_add_rcu(&p->list, &br->port_list);
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);//如果設備有載波,並且處於開啟狀態,則使能端口的stp,設置端口為指定端口,開啟端口的定時器,開啟狀態選擇
spin_unlock_bh(&br->lock);
dev_set_mtu(br->dev, br_min_mtu(br));//更新網橋設備的mtu為所有端口中最小的mtu
}
return err;
}
// 重新計算網橋
// 必要時通過stp協議開始發送配置BPDU
// 函數主要任務:
// 1.選擇端口中最小的mac地址
// 2.更新網橋id
// 注:選擇網橋端口中mac地址最小者,作為網橋id的mac部分
3.3 void br_stp_recalculate_bridge_id(struct net_bridge *br)
{
const unsigned char *addr = br_mac_zero;//0地址
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) {//遍曆所有的端口
if (addr == br_mac_zero ||//第一次是成立
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)//選擇端口中最小的mac地址
addr = p->dev->dev_addr;
}
if (memcmp(br->bridge_id.addr, addr, ETH_ALEN))//如果端口中最小的mac地址與網橋使用的mac地址不同
br_stp_change_bridge_id(br, addr);//更新網橋id
}
// 更新網橋id
// 參數:
// addr, 網橋id中的mac部分
// 調用路徑:br_stp_recalculate_bridge_id->br_stp_change_bridge_id
// 函數主要任務:
// 1.拷貝新mac地址到網橋id中
// 2.拷貝新mac地址到網橋設備描述符的addr中
// 3.更新所有指定端口,使用新的mac地址
// 4.更新網橋配置信息
// 5.重啟端口狀態選擇
// 6.如果由非根網橋變為根網橋,發送配置bpdu
// 注:根網橋dev->dev_addr,指定端口dev,使用所有端口中最小的mac地址。
3.4 static void br_stp_change_bridge_id(struct net_bridge *br,
const unsigned char *addr)
{
unsigned char oldaddr[6];
struct net_bridge_port *p;
int wasroot;
wasroot = br_is_root_bridge(br);
memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
memcpy(br->bridge_id.addr, addr, ETH_ALEN);
memcpy(br->dev->dev_addr, addr, ETH_ALEN);//設置網橋設備的mac地址為新的mac地址
list_for_each_entry(p, &br->port_list, list) {//遍曆所有的網橋端口
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))//更新使用原mac地址的端口
memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))//
memcpy(p->designated_root.addr, addr, ETH_ALEN);
}
br_configuration_update(br);//選擇根端口,指定端口,通過已有信息進行選擇
br_port_state_selection(br);//開啟端口的狀態選擇
if (br_is_root_bridge(br) && !wasroot)//網橋由非根網橋變為根網橋
br_become_root_bridge(br);//開啟topology change timer,發送設置tc標誌的配置bpdu
}
// 刪除端口
// 函數主要任務:
// 1.刪除網橋端口
// 2.重新計算網橋id
4.1 int br_del_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p = dev->br_port;
if (!p || p->br != br)
return -EINVAL;
br_sysfs_removeif(p);
del_nbp(p);
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
return 0;
}
最後更新:2017-04-03 15:22:11