閱讀385 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go Java中CallableStatement調用Oracle存儲過程總結
  下一篇:go 使用spring jdbc template簡化jdbc數據庫操作實例代碼