linux网桥--接受数据包

43 篇文章 1 订阅
2 篇文章 0 订阅

 版权声明:如有需要,可供转载,但请注明出处:https://blog.csdn.net/City_of_skey/article/details/85254786

目录

1、netif_receive_skb

2、handle_bridge

3、br_handle_frame

4、br_handle_frame_finish

5、br_forward

6、br_flood_forward

7、br_pass_frame_up


1、netif_receive_skb

当一个网卡收到数据包后机会触发硬件中断,然后调用网卡驱动程序生成skb并包skb拷贝到CPU输入队列,再触发软中断调用netif_receive_skb来处理数据包,netif_receive_skb主要调用了__netif_receive_skb,这个函数主要做以下四件事

(1)、遍历嗅探器ptype_all,有注册函数就拷贝一份数据包,比如tcpdump抓包就是这在ptype_all中注册了一个函数。

(2)、调用handle_bridge处理网桥

(3)、调用handle_macvlan处理vlan

(4)、根据协议号在ptype_base查找三层处理函数,如果是IP协议就调用ip_rcv数据包上三层

二三层简单交互图

__netif_receive_skb:

static int __netif_receive_skb(struct sk_buff *skb)
{
	struct packet_type *ptype, *pt_prev;
	struct net_device *orig_dev;
	struct net_device *master;
	struct net_device *null_or_orig;
	struct net_device *orig_or_bond;
	int ret = NET_RX_DROP;
	__be16 type;

	if (!netdev_tstamp_prequeue)
		net_timestamp_check(skb);

	if (vlan_tx_tag_present(skb) && vlan_hwaccel_do_receive(skb))
		return NET_RX_SUCCESS;

	/* if we've gotten here through NAPI, check netpoll */
	if (netpoll_receive_skb(skb))
		return NET_RX_DROP;

	if (!skb->skb_iif)
		skb->skb_iif = skb->dev->ifindex;

	/*
	 * bonding note: skbs received on inactive slaves should only
	 * be delivered to pkt handlers that are exact matches.  Also
	 * the deliver_no_wcard flag will be set.  If packet handlers
	 * are sensitive to duplicate packets these skbs will need to
	 * be dropped at the handler.  The vlan accel path may have
	 * already set the deliver_no_wcard flag.
	 */
	null_or_orig = NULL;
	orig_dev = skb->dev;
	master = ACCESS_ONCE(orig_dev->master);
	if (skb->deliver_no_wcard)
		null_or_orig = orig_dev;
	else if (master) {
		if (skb_bond_should_drop(skb, master)) {
			skb->deliver_no_wcard = 1;
			null_or_orig = orig_dev; /* deliver only exact match */
		} else
			skb->dev = master;
	}

	__get_cpu_var(softnet_data).processed++;

	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb->mac_len = skb->network_header - skb->mac_header;

	pt_prev = NULL;

	rcu_read_lock();

#ifdef CONFIG_NET_CLS_ACT
	if (skb->tc_verd & TC_NCLS) {
		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
		goto ncls;
	}
#endif

	/*网络嗅探器比如tcpdump抓包工具,就是
	在ptype_all中注册一个函数,然后复制一份数据*/
	list_for_each_entry_rcu(ptype, &ptype_all, list) {
		if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
		    ptype->dev == orig_dev) {
			if (pt_prev)
				ret = deliver_skb(skb, pt_prev, orig_dev);
			pt_prev = ptype;
		}
	}

#ifdef CONFIG_NET_CLS_ACT
	skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
	if (!skb)
		goto out;
ncls:
#endif
	/*网桥处理函数*/
	skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
	if (!skb)
		goto out;
	/*vlan处理*/
	skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
	if (!skb)
		goto out;

	/*
	 * Make sure frames received on VLAN interfaces stacked on
	 * bonding interfaces still make their way to any base bonding
	 * device that may have registered for a specific ptype.  The
	 * handler may have to adjust skb->dev and orig_dev.
	 */
	orig_or_bond = orig_dev;
	if ((skb->dev->priv_flags & IFF_802_1Q_VLAN) &&
	    (vlan_dev_real_dev(skb->dev)->priv_flags & IFF_BONDING)) {
		orig_or_bond = vlan_dev_real_dev(skb->dev);
	}

	/*根据网络协议在ptype_base中查找三层协议*/
	type = skb->protocol;
	list_for_each_entry_rcu(ptype,
			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
		if (ptype->type == type && (ptype->dev == null_or_orig ||
		     ptype->dev == skb->dev || ptype->dev == orig_dev ||
		     ptype->dev == orig_or_bond)) {
			if (pt_prev)
				ret = deliver_skb(skb, pt_prev, orig_dev);
			pt_prev = ptype;
		}
	}

	/*数据包上三层*/
	if (pt_prev) {
		/*三层处理函数,ip_rcv*/
		ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
	} else {
		kfree_skb(skb);
		/* Jamal, now you will not able to escape explaining
		 * me how you were going to use this. :-)
		 */
		ret = NET_RX_DROP;
	}

out:
	rcu_read_unlock();
	return ret;
}

2、handle_bridge

handle_bridge首先判断数据包类型,不属于网桥设备或者是环网数据直接返回。然后调用br_handle_frame_hook进入网桥处理流程,br_handle_frame_hook在网桥初始化是赋值给函数br_handle_frame。

static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
					    struct packet_type **pt_prev, int *ret,
					    struct net_device *orig_dev)
{
	struct net_bridge_port *port;

	/*数据包是环网或者设备不属于任何网桥
	就直接返回*/
	if (skb->pkt_type == PACKET_LOOPBACK ||
	    (port = rcu_dereference(skb->dev->br_port)) == NULL)
		return skb;

	if (*pt_prev) {
		*ret = deliver_skb(skb, *pt_prev, orig_dev);
		*pt_prev = NULL;
	}
	/*网桥处理入口,网桥初始化时复制给函数br_handle_frame*/
	return br_handle_frame_hook(port, skb);
}

3、br_handle_frame

netfileter框架在二层也维持了一个过滤系统,br_handle_frame首先判断数据包的类型是本机还是转发。

1、本地数据包首先要过二层netfilter的NF_BR_LOCAL_IN链上处理函数,最后调用br_handle_local_finish,如果数据包没有被丢弃,就返回继续上层协议处理。

2、如果是转发数据包,首先ebtables查找路由表的hook处理函数,如果找了就调用hook处理选了路由后返回继续上层协议处理。如果没有找到就进入二层netfilter框架的NF_BR_PRE_ROUTING链处理,处理完毕调用br_handle_frame_finish。

struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
{
	const unsigned char *dest = eth_hdr(skb)->h_dest;
	int (*rhook)(struct sk_buff *skb);

	/*判断mac地址是否有效,广播地址和00....00是非法地址*/
	if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
		goto drop;

	/*共享数据包clone一份*/
	skb = skb_share_check(skb, GFP_ATOMIC);
	if (!skb)
		return NULL;

	/*本地数据包*/
	if (unlikely(is_link_local(dest))) {
		/* Pause frames shouldn't be passed up by driver anyway */
		if (skb->protocol == htons(ETH_P_PAUSE))
			goto drop;

		/* If STP is turned off, then forward */
		if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0)
			goto forward;

		/*二层的netfilet框架进入NF_BR_LOCAL_IN链上处理函数*/
		if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
			    NULL, br_handle_local_finish))
			return NULL;	/* frame consumed by filter */
		else
			/*通过二层的netfilter后继续上层处理数据*/
			return skb;	/* continue processing */
	}
/*转发处理*/
forward:
	switch (p->state) {
	case BR_STATE_FORWARDING:
		/*ebtables查找路由的hook点*/
		rhook = rcu_dereference(br_should_route_hook);
		if (rhook != NULL) {
			/*如果找到了就处理,然后继续数据包上层处理*/
			if (rhook(skb))
				return skb;
			dest = eth_hdr(skb)->h_dest;
		}
		/* fall through */
	case BR_STATE_LEARNING:
		if (!compare_ether_addr(p->br->dev->dev_addr, dest))
			skb->pkt_type = PACKET_HOST;

		/*NF_BR_PRE_ROUTING表处理完毕调用br_handle_frame_finish*/
		NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
			br_handle_frame_finish);
		break;
	default:
drop:
		kfree_skb(skb);
	}
	return NULL;
}

4、br_handle_frame_finish

br_handle_frame_finish是经过netfilter的NF_BR_PRE_ROUTING链处理后调用,主要做以下事情:

(1)判断桥端口是否是开启的,如果没有开启就直接返回

(2)调用br_fdb_update做mac地址学习

(3)判断目的地址类型如果是多播地址就调用br_multicast_forward转发

(4)根据目的地址查找端口的转发表,如果找到了就调用br_forward从此端口转发出去,如果没有找到对应的端口就调用br_flood_forward每个端口转发一份

/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct sk_buff *skb)
{
	const unsigned char *dest = eth_hdr(skb)->h_dest;
	struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
	struct net_bridge *br;
	struct net_bridge_fdb_entry *dst;
	struct net_bridge_mdb_entry *mdst;
	struct sk_buff *skb2;

	/*桥端口是否是开启状况*/	
	if (!p || p->state == BR_STATE_DISABLED)
		goto drop;

	/* insert into forwarding database after filtering to avoid spoofing */
	br = p->br;
	/*mac地址学习,更新端口-MAC表*/
	br_fdb_update(br, p, eth_hdr(skb)->h_source);

	if (is_multicast_ether_addr(dest) &&
	    br_multicast_rcv(br, p, skb))
		goto drop;

	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_multicast_ether_addr(dest)) {
		mdst = br_mdb_get(br, skb);
		if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {
			if ((mdst && !hlist_unhashed(&mdst->mglist)) ||
			    br_multicast_is_router(br))
				skb2 = skb;
			/*多播转发处理*/
			br_multicast_forward(mdst, skb, skb2);
			skb = NULL;
			if (!skb2)
				goto out;
		} else
			skb2 = skb;

		br->dev->stats.multicast++;
		/*根据mac地址获取对应的端口-MAC表*/
	} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {
		skb2 = skb;
		/* Do not forward the packet since it's local. */
		skb = NULL;
	}

	if (skb) {
		if (dst)
			/*找到了mac对应的端口就转发*/
			br_forward(dst->dst, skb, skb2);
		else
			/*转发表中没有找到该mac对应的端口就每个端口转发一份*/
			br_flood_forward(br, skb, skb2);
	}

    /*副本数据发完本地,走三层转发*/
	if (skb2)
		return br_pass_frame_up(skb2);

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

5、br_forward

网桥转发处理走br_forward,最终处理的是__br_forward函数

/* 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);
}

__br_forward函数数据包先通过二层netfilter框架的NF_BR_FORWARD链处理再调用br_forward_finish函数继续。

__br_forward:

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;
	}

	indev = skb->dev;
	skb->dev = to->dev;
	skb_forward_csum(skb);

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

br_forward_finish函数要通过NF_BR_LOCAL_OUT链的过滤处理在调用br_dev_queue_push_xmit继续处理。

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);

}

br_dev_queue_push_xmit函数最终调用dev_queue_xmit通过网卡驱动将数据包发送出去。

br_dev_queue_push_xmit:

int br_dev_queue_push_xmit(struct sk_buff *skb)
{
	/* drop mtu oversized packets except gso */
	if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
		kfree_skb(skb);
	else {
		/* ip_fragment doesn't copy the MAC header */
		if (nf_bridge_maybe_copy_header(skb))
			kfree_skb(skb);
		else {
			skb_push(skb, ETH_HLEN);
			dev_queue_xmit(skb);		//驱动转发出去
		}
	}

	return 0;
}

6、br_flood_forward

br_flood_forward处理方法是遍历网桥下面的所有端口,每个端口调用__br_forward发送一次。

/* 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);
}

br_flood:

/* 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))
{
	struct net_bridge_port *p;
	struct net_bridge_port *prev;

	prev = NULL;

	/*遍历网桥下端口链表port_list每个端口调用__br_forward发送一次*/
	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);
}

7、br_pass_frame_up

br_pass_frame_up会复制一份副本再次进入netif_receive_skb函数走三层转发处理,不过这次不会在进入handle_bridge,因为skb->dev是网桥设备而不是一个端口设备。

static int br_pass_frame_up(struct sk_buff *skb)
{
	struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
	struct net_bridge *br = netdev_priv(brdev);
	struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);

	/*统计网桥收包数*/
	brstats->rx_packets++;
	brstats->rx_bytes += skb->len;

	/*将数据包的设备改成网桥的设备*/
	indev = skb->dev;
	skb->dev = brdev;

	/*通过NF_BR_LOCAL_IN链处理再次进入netif_receive_skb函数*/
	return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
		       netif_receive_skb);
}

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值