网络子系统34_网桥设备的传输与接收

简介:
//	网桥设备驱动程序的hard_start_xmit函数

//	函数主要任务:
//		1.广播或多播地址,在所有端口上扩散
//		2.存在转发项,在指定端口上发送
//		3.没有找到转发项,在所有端口上扩散
1.1 int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct net_bridge *br = netdev_priv(dev);
	const unsigned char *dest = skb->data;
	struct net_bridge_fdb_entry *dst;
	//更新网桥设备的统计信息
	br->statistics.tx_packets++;
	br->statistics.tx_bytes += skb->len;
	//更新l2头位置
	skb->mac.raw = skb->data;
	skb_pull(skb, ETH_HLEN);

	rcu_read_lock();
	if (dest[0] & 1)//l2地址的第一个字节的第一位为1,则为多播地址 
		br_flood_deliver(br, skb, 0);//在所有端口上发送此报文
	else if ((dst = __br_fdb_get(br, dest)) != NULL)//如果转发数据中存在目标地址的记录
		br_deliver(dst->dst, skb);//通过与目标地址相关的端口发送
	else
		br_flood_deliver(br, skb, 0);

	rcu_read_unlock();
	return 0;
}

//	在所有端口扩散skb
//	参数:__packet_hook, __br_deliver

//	调用路径:br_dev_xmit->br_flood_deliver->br_flood
1.3 static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
	void (*__packet_hook)(const struct net_bridge_port *p, 
			      struct sk_buff *skb))
{
	struct net_bridge_port *p;
	struct net_bridge_port *prev;
	//指示是否复制一份skb,br_dev_xmit->br_flood_deliver->br_flood此调用路径clone=0
	if (clone) {
		struct sk_buff *skb2;

		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
			br->statistics.tx_dropped++;
			return;
		}

		skb = skb2;
	}

	prev = NULL;
	//遍历所有的端口
	list_for_each_entry_rcu(p, &br->port_list, list) {
		if (should_deliver(p, skb)) {//skb的输出设备非此端口,并且此端口处于转发模式
			if (prev != NULL) {//在第一次运行时,prev=NULL,直接复制prev=p
				struct sk_buff *skb2;

				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {//克隆sk_buff,共享packet data
					br->statistics.tx_dropped++;
					kfree_skb(skb);
					return;
				}

				__packet_hook(prev, skb2);//调用__br_deliver,执行一系列的netfilter调用点,最后由dev_queue_xmit完成传输
			}

			prev = p;
		}
	}

	if (prev != NULL) {
		__packet_hook(prev, skb);//对最后一个端口调用__br_deliver
		return;
	}

	kfree_skb(skb);
}

//	在网桥端口传输
//	调用路径:br_flood->hook->br_dev_queue_push_xmit
1.6 int br_dev_queue_push_xmit(struct sk_buff *skb)
{
	if (skb->len > skb->dev->mtu)//如果skb的长度大于出口设备的mtu,则直接丢弃封包 
		kfree_skb(skb);
	else {
		skb_push(skb, ETH_HLEN);
		dev_queue_xmit(skb);//输出
	}

	return 0;
}

//	判断端口是否应该发送数据帧
//	端口发送数据帧的条件:
//		1.skb的入口设备非此端口
//		2.端口处于转发模式
1.7 static inline int should_deliver(const struct net_bridge_port *p, 
				 const struct sk_buff *skb)
{
	if (skb->dev == p->dev ||//skb的接收端口为此端口
	    p->state != BR_STATE_FORWARDING)//端口非转发模式
		return 0;

	return 1;
}


//	网桥入口数据帧处理
//	调用路径:netif_receive_skb->handle_bridge
//
//	由网桥接收入口数据的条件:
//		1. 入口设备非回环地址
//		2. 入口设备为网桥端口
2.1 static __inline__ int handle_bridge(struct sk_buff **pskb,
				    struct packet_type **pt_prev, int *ret)
{
	struct net_bridge_port *port;

	if ((*pskb)->pkt_type == PACKET_LOOPBACK ||
	    (port = rcu_dereference((*pskb)->dev->br_port)) == NULL)//如果设备为网桥的端口,则dev->br_port为网桥端口控制块
		return 0;

	if (*pt_prev) {
		*ret = deliver_skb(*pskb, *pt_prev);
		*pt_prev = NULL;
	} 
	//此设备为网桥的端口
	return br_handle_frame_hook(port, pskb);
}


//	网桥处理入口流量
//	参数:
//		p,接收到此入口流量的端口
//		pskb, 入口数据帧
//
//	返回值:
//		0,网桥没有处理此pskb,netif_receive_skb继续处理pskb
//		1,网桥已经处理此pskb,netif_receive_skb无需继续处理此pskb
//
//	调用路径 : netif_receive_skb->handle_bridge->br_handle_frame
//	
//	函数主要任务:
//		1.检查分包合法性:
//			1.1 接收端口开启状态
//			1.2 源地址非多播或广播地址
//		2.向转发数据库添加转发项
//		3.识别配置bpdu,处理配置bpdu
//		4.决定入口流量被路由还是桥接
//		5.如果数据帧需要被桥接,则由桥接处理程序继续处理。

//	注:
//		1.只有转发态(BR_STATE_FORWARDING),学习态(BR_STATE_LEARNING)的网桥端口,可以向转发数据库添加转发项。
//		2.bpdu的识别办法:以太网帧的目的地址为[0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]
//		3.决定入口流量被路由还是桥接,通过ebtable_broute决定
//		4.只有转发态(BR_STATE_FORWARDING)的网桥端口,可以处理普通入口流量。
			
2.2 int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
{
	struct sk_buff *skb = *pskb;
	const unsigned char *dest = eth_hdr(skb)->h_dest;

	if (p->state == BR_STATE_DISABLED)//端口被关闭
		goto err;

	if (eth_hdr(skb)->h_source[0] & 1)//源地址为广播地址,说明错误
		goto err;

	if (p->state == BR_STATE_LEARNING ||//如果当前端口处于学习状态,或者转发状态
	    p->state == BR_STATE_FORWARDING)
		br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0);//在转发数据库中添加一条新转发项,或者更新现有的转发项

	if (p->br->stp_enabled &&//网桥的stp协议开启
	    !memcmp(dest, bridge_ula, 5) &&//网桥组播地址的前5个字节
	    !(dest[5] & 0xF0)) {//bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },通过入口帧的目标地址识别bpdu数据帧
		if (!dest[5]) {
			NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, 
				NULL, br_stp_handle_bpdu);//入口bpdu的数据帧,可以被任何端口接收,只要这个端口没有通过管理性手段关闭
			return 1;
		}
	}

	else if (p->state == BR_STATE_FORWARDING) {//当前端口处于转发状态,一个封包是被路由还是桥接,通过br_should_route_hook函数决定
		if (br_should_route_hook) {//在ebtable_broute模块中被设置
			if (br_should_route_hook(pskb)) 
				return 0;
			skb = *pskb;
			dest = eth_hdr(skb)->h_dest;
		}

		if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))//目标地址为本地
			skb->pkt_type = PACKET_HOST;

		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
			br_handle_frame_finish);//默认情况下,被绑定的设备所接收到的网络流量会分配给它的指定网桥
		return 1;
	}

err:
	kfree_skb(skb);
	return 1;
}

//	桥接入口流量

//	调用路径: br_handle_frame->br_handle_frame_finish

//	函数主要任务:
//		1.网桥端口均处于混杂模式,向网桥本地接收模块传递一份此数据帧
//		2.多播或广播目的地址,在除接收端口外的所有端口,扩散此入口数据帧,退出
//		3.如果,数据帧目的地址为本机,向网桥本地接收模块传递一份此数据帧,退出
//		4.否则,如果网桥知道能到达目的地址的输出端口,从输出端口发送此数据帧,退出
//		5.否则,如果网桥不知道使用哪个输出端口,向所有端口扩散,退出
2.3 int br_handle_frame_finish(struct sk_buff *skb)
{
	const unsigned char *dest = eth_hdr(skb)->h_dest;
	struct net_bridge_port *p = skb->dev->br_port;
	struct net_bridge *br = p->br;
	struct net_bridge_fdb_entry *dst;
	int passedup = 0;

	if (br->dev->flags & IFF_PROMISC) {//设备处于混杂模式
		struct sk_buff *skb2;

		skb2 = skb_clone(skb, GFP_ATOMIC);//复制一份sk_buff,共享packet data
		if (skb2 != NULL) {
			passedup = 1;
			br_pass_frame_up(br, skb2);//向上层协议传递skb
		}
	}

	if (dest[0] & 1) {//以太网多播地址
		br_flood_forward(br, skb, !passedup);//从各个端口扩散此skb
		if (!passedup)
			br_pass_frame_up(br, skb);//如果处于混杂模式,则已经向上传递了一份skb
		goto out;
	}

	dst = __br_fdb_get(br, dest);//非多播地址的skb,查询转发数据库
	if (dst != NULL && dst->is_local) {//本地地址
		if (!passedup)
			br_pass_frame_up(br, skb);//向上层协议传递一份skb,由于网桥端口为混杂模式,之前会传递一份,所以此处不会再传递
		else
			kfree_skb(skb);
		goto out;
	}

	if (dst != NULL) {
		br_forward(dst->dst, skb);//非本地地址,从通往目标地址的端口发送此skb
		goto out;
	}

	br_flood_forward(br, skb, 0);//非以太网多播,没有明确的出口设备,从所有端口上扩散

out:
	return 0;
}

//	网桥向本地传递流量
//	调用路径:br_handle_frame_finish->br_pass_frame_up

//	函数主要任务:
//		1.skb->dev设置为网桥设备,为skb重新走一遍netif_receive_skb

//	注:网桥端口处于混杂模式,所有数据帧均会向上传递,并且一个数据帧,只会向本机传递一次。
2.4 static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
	struct net_device *indev;

	//增加统计数据
	br->statistics.rx_packets++;
	br->statistics.rx_bytes += skb->len;

	indev = skb->dev;
	skb->dev = br->dev;//修改接收此skb的设备为网桥设备

	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
			br_pass_frame_up_finish);
}

//	网桥向本地传递流量
//		使用积压设备的方式
//	调用路径:br_pass_frame_up->br_pass_frame_up_finish
2.5 static int br_pass_frame_up_finish(struct sk_buff *skb)
{
	//挂载积压设备到poll_list,skb到input_pkt_queueu, 触发接收软中断
	netif_rx(skb);
	return 0;
}

目录
相关文章
|
24天前
|
安全 网络安全 网络虚拟化
【软件设计师备考 专题 】常用网络设备和各类通信设备(一)
【软件设计师备考 专题 】常用网络设备和各类通信设备
97 2
|
1月前
|
SQL 安全 网络协议
网络安全产品之认识漏洞扫描设备
为了保障系统的安全性,需要及时发现和修复漏洞。这可以通过漏洞扫描设备等工具进行自动化检测和修复,同时也可以加强安全意识和培训,提高人员的安全防范能力。虽然无法完全避免漏洞的存在,但通过采取有效的措施可以大大减少漏洞的数量和危害程度,保障系统的安全性和稳定性。本文让我们一起来认识漏洞扫描设备。
49 0
|
27天前
|
监控 Linux Shell
【Shell 命令集合 网络通讯 】Linux管理终端设备的登录过程 getty命令 使用指南
【Shell 命令集合 网络通讯 】Linux管理终端设备的登录过程 getty命令 使用指南
31 0
|
27天前
|
安全 Shell Linux
【Shell 命令集合 网络通讯 】Linux 打开终端设备 mingetty命令 使用指南
【Shell 命令集合 网络通讯 】Linux 打开终端设备 mingetty命令 使用指南
38 0
|
9天前
|
传感器 监控 安全
|
9天前
|
边缘计算 网络虚拟化 虚拟化
虚拟网络设备性能优化
在现代网络架构中,虚拟网络设备扮演着越来越重要的角色🌐,特别是在云计算☁️和容器化技术📦广泛应用的背景下。虚拟网络设备如虚拟以太网设备(veth)、虚拟交换机(vSwitch)、和虚拟路由器等,提供了灵活的网络连接和隔离方案🔗。然而,与物理网络设备相比,虚拟网络设备在处理能力💪、带宽利用率📈和延迟⏳方面可能存在性能瓶颈。因此,性能优化成为了虚拟网络设备管理中的一个重要议题🛠️。本文将探讨虚拟网络设备的性能优化手段,帮助网络管理员更有效地利用这些设备。
|
9天前
|
安全 网络安全 网络虚拟化
虚拟网络设备与网络安全:深入分析与实践应用
在数字化时代📲,网络安全🔒成为了企业和个人防御体系中不可或缺的一部分。随着网络攻击的日益复杂和频繁🔥,传统的物理网络安全措施已经无法满足快速发展的需求。虚拟网络设备🖧,作为网络架构中的重要组成部分,通过提供灵活的配置和强大的隔离能力🛡️,为网络安全提供了新的保障。本文将从多个维度深入分析虚拟网络设备是如何保障网络安全的,以及它们的实际意义和应用场景。
|
24天前
|
安全 网络安全 网络虚拟化
【软件设计师备考 专题 】常用网络设备和各类通信设备(三)
【软件设计师备考 专题 】常用网络设备和各类通信设备
33 0
|
24天前
|
安全 算法 网络安全
【软件设计师备考 专题 】常用网络设备和各类通信设备(二)
【软件设计师备考 专题 】常用网络设备和各类通信设备
41 2
|
27天前
|
监控 Shell Linux
【Shell 命令集合 网络通讯 】Linux 发送和接收传真 efax命令 使用指南
【Shell 命令集合 网络通讯 】Linux 发送和接收传真 efax命令 使用指南
28 0

热门文章

最新文章