网络子系统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;
}



目录
相关文章
|
22天前
|
Docker 容器
docker中默认桥接网络
【10月更文挑战第7天】
90 62
|
18天前
|
弹性计算 Kubernetes 网络协议
阿里云弹性网络接口技术的容器网络基础教程
阿里云弹性网络接口技术的容器网络基础教程
阿里云弹性网络接口技术的容器网络基础教程
|
12天前
|
存储 缓存 Ubuntu
配置网络接口的“IP”命令10个
【10月更文挑战第18天】配置网络接口的“IP”命令10个
36 0
|
2月前
|
KVM 虚拟化
KVM虚拟机的桥接网络
文章主要介绍了KVM虚拟机的NAT和桥接网络类型的工作原理、配置方法以及如何进行网络模式的切换。
38 3
KVM虚拟机的桥接网络
|
3月前
|
安全 网络性能优化 网络安全
别再让网络瓶颈困扰你!掌握这十种交换机接口模式,提升你的网络布局技能
【8月更文挑战第23天】交换机作为网络核心,其接口模式直接影响网络布局与性能。本文介绍了十大常见接口模式及其配置实例,包括基础接入模式、优化布线的干道模式、动态学习相邻交换机VLAN信息的动态中继协议模式、固定分配VLAN的静态接入模式、确保语音优先传输的语音VLAN模式、指定默认VLAN的native模式、增加带宽与可靠性的链路聚合及EtherChannel模式、保障网络安全的端口安全模式以及确保关键业务流畅传输的QoS模式。理解并掌握这些模式对于构建高效稳定的网络至关重要。
71 1
|
3月前
|
监控 Linux
在Linux中,如何查看网络接口的状态?
在Linux中,如何查看网络接口的状态?
|
3月前
|
网络协议 安全 网络安全
网络术语、接口和协议简介
网络术语、接口和协议简介
49 1
|
2月前
|
域名解析 负载均衡 网络协议
Linux网络接口配置不当所带来的影响
总而言之,Linux网络接口的恰当配置是保证网络稳定性、性能和安全性的基础。通过遵循最佳实践和定期维护,可以最大程度地减少配置错误带来的负面影响。
118 0
|
3月前
|
网络协议 Linux KVM
在Linux中,如何配置网络桥接?
在Linux中,如何配置网络桥接?
|
3月前
|
网络协议 Ubuntu Linux
在Linux中, 如何配置网络接口?
在Linux中, 如何配置网络接口?