网桥工作在集线器(hub)模式

集线器hub不同于Linux网桥实现的交换机,集线器不学习数据包的MAC地址,没有二层转发表FDB,对于接收到的数据包总是泛洪到所有的子接口上。相当于没有学习到任何MAC地址(FDB为空)的网桥交换机。

两个思路修改网桥为HUB工作模式,一是保持FDB表为空,不添加任何表项,将FDB添加表项函数去除;另外也可以单独写一个网桥接收处理函数,直接调用flood功能函数。以下代码采用第一种方式,实现也比较简单:

首先在向网桥添加子接口(br_add_if)时,不使用原本的网桥处理函数br_handle_frame,注册一个新的hub功能处理函数。

err = netdev_rx_handler_register(dev, br_handle_hub_frame, p);

新的处理函数,主要有几个部分,一个是合法性检查;一个是初始化skb的brdev和br_nf_bypass成员;最后就是在网桥上泛洪和向本机发送一份数据包。


rx_handler_result_t br_handle_hub_frame(struct sk_buff **pskb)
{
	struct net_bridge_port *p;
	struct sk_buff *skb = *pskb;
	struct sk_buff *skb2 = skb;   //sent to host
	int unicast = 0;      //for a hub, all is broadcast

	if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
		return RX_HANDLER_PASS;
	if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
		goto drop;
	skb = skb_share_check(skb, GFP_ATOMIC);
	if (!skb)
		return RX_HANDLER_CONSUMED;

	p = br_port_get_rcu(skb->dev);
	BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
	BR_INPUT_SKB_CB(skb)->br_nf_bypass = 1;

	br_flood_forward(p->br, skb, skb2, unicast);
	br_pass_frame_up(skb2);

	return RX_HANDLER_CONSUMED;
drop:
	kfree_skb(skb);
	return RX_HANDLER_CONSUMED;
}


需要说明的是泛洪函数的最后一个参数unicast始终设置为零,表示对于一个hub来说无所谓单播,所有的数据包都是广播。相反如果是单播报文的话,通过bridge命令可以关闭某个网桥子接口的单播泛洪功能,将会破坏hub功能。

bridge link set dev eth0 flood off


对于集线器hub而言,其实不需要向本机发送一份数据包。此处这么做,网桥本身就可以接收数据包,可配置IP地址,可做管理接口,算是hub的一个扩展功能。

另外br_nf_bypass变量为增加的一个自定义变量。由于不管是转发还是本机接收,都会遇到bridge网桥的hook点,NF_BR_FORWARD或者NF_BR_LOCAL_IN,难保证用户不会增加ebtables的过滤策略,致使hub泛洪功能异常。增加变量br_nf_bypass掉过hook点函数,如下__br_forward函数。


static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
	if (unlikely(BR_INPUT_SKB_CB(skb)->br_nf_bypass))
		br_forward_finish(NULL, skb);
	else
		NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb, indev, skb->dev, br_forward_finish);
}


最后注意,网桥的vlan filtering功能也不要打开(默认是关闭状态)。


内核版本

Linux-4.0


猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/81039068