網絡子係統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