桥数据包处理函数——br_handle_frame_finish(七)

上一节我们了解到,数据包如何走进桥,这一节我们简单看看,入口帧处理函数br_handle_frame_finish.
作用:br_handle_frame_finish函数主要是决策将不同类别的数据包做不同的分发路径。

其函数处理的过程如下图所示:




首先判断该数据包是否符合桥转发的条件:
    (1)桥端口状态是否是开启状态,如果没有开启则丢掉数据包
    (2)是否允许从该桥上转发,如果不允许,则直接返回0
获得桥转发的条件以后,开始判断数据包的类型:
    (1)判断此时桥的标志为允许做哪些事情,学习还是扩展
  如果学习的标志位被至位,则更新数据转发表。否则继续向下走
    (2)根据多播或者广播报文的类型决定数据包的去留
(3)判断此时端口的状态,如果是学习状态,则将数据包丢弃
  (要注意的是:桥的端口状态(和上面的flag不冲突,上面的flag表示网桥可以做的事情)state表示网桥端口所处于的状态)
在处理完一些需要预备的事情之后,就要为数据包的转发开始做准备了
    (1)网桥设备是否处于混杂模式,如果是则建立副本,为发往本地做个备份
  (注意的是,所有网桥端口绑定的设备都会处于混杂模式,因为 网桥运行必须此模式。但除非明确的对其进行配置,否则网桥自己是不会处于混杂模式                的)
(2)在次判断广播还是多播地址
   广播地址:仅仅设置副本,进行广播转发和发往本地
多播地址:先查多播地址转发表,如果存在,设置副本,进行多播转发,原始数据包指向NULL,如果已经传送至本地,则会释放副本,不进行本地转发,否则重新转发到本地
(3)不是广播或者多播
   判断是否本地地址,如果是本地地址,则将原始数据包指向NULL,发往本地。
    否则进行数据包转发


/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	const unsigned char *dest = eth_hdr(skb)->h_dest;
	struct net_bridge_port *p = br_port_get_rcu(skb->dev);
	struct net_bridge *br;
	struct net_bridge_fdb_entry *dst;
	struct net_bridge_mdb_entry *mdst;
	struct sk_buff *skb2;
	bool unicast = true;
	u16 vid = 0;

	if (!p || p->state == BR_STATE_DISABLED)
		goto drop;
    /*判断是否允许进入桥内,如果没有开启vlan则所有的数据包都可以进入,
      如果开启了vlan则根据vlan相应的规则,从桥上进行数据包转发*/
	if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
		goto out;

	/* insert into forwarding database after filtering to avoid spoofing */
	br = p->br;
	/*如果可以学习,则学习数据包的原地址*/
	if (p->flags & BR_LEARNING)
		br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);

	if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
	    br_multicast_rcv(br, p, skb, vid))
		goto drop;
    /*桥的端口状态(和上面的flag不冲突,上面的flag表示网桥可以做的事情)
	  state表示网桥端口所处于的状态*/
	if (p->state == BR_STATE_LEARNING)
		goto drop;

	BR_INPUT_SKB_CB(skb)->brdev = br->dev;

	/* The packet skb2 goes to the local host (NULL to skip). */
	skb2 = NULL;

	if (br->dev->flags & IFF_PROMISC)
		skb2 = skb;

	dst = NULL;

	if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP))
		br_do_proxy_arp(skb, br, vid, p);

	if (is_broadcast_ether_addr(dest)) {
		skb2 = skb;
		unicast = false;
	} else if (is_multicast_ether_addr(dest)) {
		mdst = br_mdb_get(br, skb, vid);
		if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
		    br_multicast_querier_exists(br, eth_hdr(skb))) {
			if ((mdst && mdst->mglist) ||
			    br_multicast_is_router(br))
				skb2 = skb;
			br_multicast_forward(mdst, skb, skb2);
			skb = NULL;
			if (!skb2)
				goto out;
		} else
			skb2 = skb;

		unicast = false;
		br->dev->stats.multicast++;
	} else if ((dst = __br_fdb_get(br, dest, vid)) &&
			dst->is_local) {
		skb2 = skb;
		/* Do not forward the packet since it's local. */
		skb = NULL;
	}

	if (skb) {
		if (dst) {
			dst->used = jiffies;
			br_forward(dst->dst, skb, skb2);
		} else
			br_flood_forward(br, skb, skb2, unicast);/*扩撒帧*/
	}
    /*将副本传入本地*/
	if (skb2)
		return br_pass_frame_up(skb2);

out:
	return 0;
drop:
	kfree_skb(skb);
	goto out;
}

数据包发送有两个地方,一个是转发出去br_forward或者br_flood_forward,一个是发往本地br_pass_frame_up,关于数据包的转发,会在下节看到


相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页