linux 网桥代码分析之网桥数据转发函数分析Ⅵ

目录

1 指定端口转发数据 br_forward()

1.1 是否允许转发 should_deliver()

1.2 转发函数 __br_forward()

1.2.1 公共转发接口 br_forward_finish()

2 所有端口发送数据 br_flood_forward()

3 本地数据指定端口发送 br_deliver()

3.1 本地数据发送 __br_deliver()

4 本地数据所有端口发送 br_flood_deliver()


网桥收到非本地的报文时需要将报文进行转发,或者本地有数据需要通过网桥发送出去,两者均属网桥转发内容范畴。

1 指定端口转发数据 br_forward()

当接收到的报文找到了转发表项后,就需要从指定的转发端口 const struct net_bridge_port *to,将报文转发出去,主要逻辑:

  1. 判断是否可以从端口to中转发
  2. 根据需要判断是否需要复制一份报文再进行发送
/* called with rcu_read_lock */
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
{
	if (should_deliver(to, skb)) {
		if (skb0)
			deliver_clone(to, skb, __br_forward);
		else
			__br_forward(to, skb);
		return;
	}

	if (!skb0)
		kfree_skb(skb);
}

1.1 是否允许转发 should_deliver()

should_deliver()的作用是判断是否将数据包从网桥端口p转发出去,符合转发的条件为:

  1. 网桥端口的flag为BR_HAIRPIN_MODE并且网桥端口的状态为forward时,则符合转发条件
  2. 数据包入口端口与出口端口不同,且网桥端口的状态为forward。
/* Don't forward packets to originating port or forwarding diasabled */
static inline int should_deliver(const struct net_bridge_port *p,
				 const struct sk_buff *skb)
{
	return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
		br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) &&
		p->state == BR_STATE_FORWARDING);
}

1.2 转发函数 __br_forward()

  1. 修改 skb 指向的 net dev
  2. 调用 NF_HOOK 处理 forward 链上的规则,并对允许通过的数据包调用函数 br_forward_finish() 进行后续的处理
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
	struct net_device *indev;

	if (skb_warn_if_lro(skb)) {
		kfree_skb(skb);
		return;
	}
	//获取vlan组,这个组中有许多的vlanid,br_handle_vlan函数就是要在这个组中查找自己的vid
	//添加vlan的相关配置
	skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
	if (!skb)
		return;

	indev = skb->dev;//记录数据包的原始收包网络设备
	skb->dev = to->dev;//将skb的dev修改为出口网络设备
	skb_forward_csum(skb);//如果有ip层校验和策略,则修改为NONE

	NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
		br_forward_finish);
}

1.2.1 公共转发接口 br_forward_finish()

int br_forward_finish(struct sk_buff *skb)
{
	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
		       br_dev_queue_push_xmit);

}

int br_dev_queue_push_xmit(struct sk_buff *skb)
{
	/* ip_fragment doesn't copy the MAC header */
	if (nf_bridge_maybe_copy_header(skb) ||
	    (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))) {
		kfree_skb(skb);
	} else {
		skb_push(skb, ETH_HLEN);
		br_drop_fake_rtable(skb);
		dev_queue_xmit(skb);
	}

	return 0;
}

2 所有端口发送数据 br_flood_forward()

当收到的数据包为找到指定的转发表项时,则需要对所有的端口进行转发,通过函数should_deliver() 可以将接收端口剔除在外。具体是通过调用接口 br_flood() 实现,br_flood()实现也很简单就是遍历网桥下的所有端口依次进行报文转发。

/* called under bridge lock */
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
		      struct sk_buff *skb2)
{
	br_flood(br, skb, skb2, __br_forward);
}

/* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb,
		     struct sk_buff *skb0,
		     void (*__packet_hook)(const struct net_bridge_port *p,
					   struct sk_buff *skb))//__br_deliver,__br_forward
{
	struct net_bridge_port *p;
	struct net_bridge_port *prev;

	prev = NULL;

	list_for_each_entry_rcu(p, &br->port_list, list) {
		prev = maybe_deliver(prev, p, skb, __packet_hook);
		if (IS_ERR(prev))
			goto out;
	}

	if (!prev)
		goto out;

	if (skb0)
		deliver_clone(prev, skb, __packet_hook);
	else
		__packet_hook(prev, skb);
	return;

out:
	if (!skb0)
		kfree_skb(skb);
}

3 本地数据指定端口发送 br_deliver()

/* called with rcu_read_lock */
void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
	if (to && should_deliver(to, skb)) {
		__br_deliver(to, skb);
		return;
	}

	kfree_skb(skb);
}

3.1 本地数据发送 __br_deliver()

和 __br_forward()接口的最大区别是数据是由本地发生出去的,因此需要经过的是 NF_BR_LOCAL_OUT 链而非 NF_BR_FORWARD 链,主要逻辑如下:

  1. 设置skb->dev执行要转发数据的设备dev
  2. 调用NF_HOOK,处理网桥防火墙中调用out链中的规则
  3. 若数据包没有被丢弃,则 br_forward_finish() 进行转发处理
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
	skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb);
	if (!skb)
		return;

	skb->dev = to->dev;

	if (unlikely(netpoll_tx_running(to->br->dev))) {
		if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
			kfree_skb(skb);
		else {
			skb_push(skb, ETH_HLEN);
			br_netpoll_send_skb(to, skb);
		}
		return;
	}

	NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
		br_forward_finish);
}

4 本地数据所有端口发送 br_flood_deliver()

主要是调用函数 br_flood,并注册 br_flood 的回调处理函数为 __br_deliver()

/* called with rcu_read_lock */
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
{
	br_flood(br, skb, NULL, __br_deliver);
}

由于网桥转发数据和从本地发送的数据逻辑基本相同,因此最新的内核版本已经将两组接口进行整合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值