云计算网络之--linux bridge 详解

Posted hello-Will

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了云计算网络之--linux bridge 详解相关的知识,希望对你有一定的参考价值。

linux bridge 是什么?

通俗的说linux bridge可以比作一个简单的二层虚拟交换机,集成在linux里面。像普通交换机一样,其他端口可以连接bridge,终端虚拟设备通过接入bridge实现互相通信和外部设备的通信。
linux bridge主要包括四个部分,这也是简单二层物理交换机的必备部分,可以说是虚拟了物理交换机的基础业务。

1:网络端口(或接口)集:用于将终端交换机之间的流量转发到网络中的其他主机。

2:控制平面:用于运行生成树协议(STP),该协议计算最小生成树,以防止环路使网络崩溃。

3:转发平面:用于处理来自端口的输入帧,通过基于MAC学习数据库进行转发决策,将输入帧转发至网络端口。

4:MAC学习数据库:用于跟踪LAN中的主机位置,记录mac 地址端口等信息用于转发,如下所示

:~$ bridge -d fdb
33:33:00:00:00:01 dev eth2 self permanent
02:42:6c:25:91:03 dev br-4b26bd39c37a vlan 1 master br-4b26bd39c37a permanent
02:42:6c:25:91:03 dev br-4b26bd39c37a master br-4b26bd39c37a permanent
02:42:19:0d:0e:8e dev docker0 vlan 1 master docker0 permanent
02:42:19:0d:0e:8e dev docker0 master docker0 permanent

对于每一个单播mac地址,网桥根据维护的mac地址表中匹配的mac地址的端口进行转发,如果没有找到,它会包这个单播报文广播到除了接收端口外的所有端口。

linux bridge how to 实现?

有三个子系统可以实现bridges:

ioctl: 这个接口可以用来创建或者销毁网桥,把端口从网桥上添加或者删除。
sysfs: 管理网桥和端口的特殊参数
netlink: 使用AF_NETLINK地址族的基于异步队列的通信也可以用于与网桥进行交互。

这里我们重点介绍ioctl

创建一个网桥

可以通过ioctl命令SIOCBRADDBR来实现创建一个网桥,如下面使用bridge-utils 的brctl所示:

~$ sudo strace brctl addbr br1
execve("/sbin/brctl", ["brctl", "addbr", "br1"], [/* 16 vars */]) = 0
...
ioctl(3, SIOCBRADDBR, 0x7fff2eae9966)   = 0
...

注意,此时没有设备可以处理ioctl命令,因此ioctl命令由存根方法处理: br_ioctl_deviceless_stub, 然后调用br_add_bridge. 再调用alloc_netdev, 最后调用alloc_netdev_mqs.
br_ioctl_deviceless_stub
|- br_add_bridge
|- alloc_netdev
|- alloc_netdev_mqs // creates the network device
|- br_dev_setup // sets br_dev_ioctl handler

alloc_netdev 还通过br_dev_setup初始化 netdevice . 这里还包括做bridge 的ioctl 调用处理,即添加或者删除接口的处理

int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 
    ...
    switch(cmd) 
	case SIOCBRADDIF:
	case SIOCBRDELIF:
		return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
    ...
    
    ..

添加一个接口

在 br_dev_ioctl代码里面看到 ,bridge 可以用 ioctl command SIOCBRADDIF去添加接口. 验证如下

~$ sudo strace brctl addif br0 veth0
execve("/sbin/brctl", ["brctl", "addif", "br0", "veth0"], [/* 16 vars */]) = 0
...
#gets the index number of virtual ethernet device.
ioctl(4, SIOCGIFINDEX, ifr_name="veth0", ifr_index=5) = 0
close(4)
 # add the interface to bridge.
ioctl(3, SIOCBRADDIF, 0x7fff75bfe5f0)   = 0
...

br_add_if

br_add_if 通过分配一个新的 net_bridge_port 结构体来创建然后部署一个新的接口到网桥上, 初始化接口,配置接口接收所有报文,把地址信息添加到本地转发表fdb中,绑定端口到网桥上。

/* Truncated version */
int br_add_if(struct net_bridge *br, struct net_device *dev)

	struct net_bridge_port *p;
	/* Don't allow bridging non-ethernet like devices */
    ...
	/* No bridging of bridges */
    ...
	p = new_nbp(br, dev);
    ...
	call_netdevice_notifiers(NETDEV_JOIN, dev);
	err = dev_set_promiscuity(dev, 1);
	err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
				   SYSFS_BRIDGE_PORT_ATTR);
    ...
	err = netdev_rx_handler_register(dev, br_handle_frame, p);
    /* Make entry in forwarding database*/
	if (br_fdb_insert(br, p, dev->dev_addr, 0))
		...
    ...

Only ethernet like devices can be added to bridge, as bridge is a layer 2 device.
Bridges cannot be added to a bridge.
只有类似以太网的设备能加入到bridge, 应为bridge是个二层设备,bridges之间不能被添加(不过可以同veth pair实现bridges之间的通信,pair中的一边分别加入不同的bridge)
新接口被设备成混杂模式: dev_set_promiscuity(dev, 1)

Finally, br_add_if method calls netdev_rx_handler_register, that sets the rx_handler of the interface to br_handle_frame
最后br_add_if方法通过调用netdev_rx_handler_register 设置rx_handler br_handle_frame
最终你可以看到bridge上的一个接口。

数据包处理流程


Frame processing starts with device-independent network code, in __netif_receive_skb which calls the rx_handler of the interface, that was set to br_handle_frame at the time of adding the interface to bridge.
数据包的处理开始与设备相关的代码__netif_receive_skb,然后调用加入到bridge时候注册的rx_handler 来进行下一步的处理br_handle_frame 。

br_handle_frame进行初始处理,任何前缀为01-80-C2-00-00的地址都是控制报文,可能需要特殊处理。从br_handle_frame中的注释中:

    /*
    * See IEEE 802.1D Table 7-10 Reserved addresses
    *
    * Assignment		 		Value
    * Bridge Group Address		01-80-C2-00-00-00
    * (MAC Control) 802.3		01-80-C2-00-00-01
    * (Link Aggregation) 802.3	        01-80-C2-00-00-02
    * 802.1X PAE address		01-80-C2-00-00-03
    *
    * 802.1AB LLDP 		01-80-C2-00-00-0E
    *
    * Others reserved for future standardization
    */

在该方法中,请注意,如果在网桥上启用STP或分别禁用STP,则stp消息将传递到上层或转发。最后,如果做出转发决定,则将数据包传递到br_handle_frame_finish,在此进行实际转发。

/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct sk_buff *skb)

    struct net_bridge_port *p = br_port_get_rcu(skb->dev);
    ...
	/* insert into forwarding database after filtering to avoid spoofing */
	br = p->br;
	br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
	if (p->state == BR_STATE_LEARNING)
		goto drop;
	/* 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_broadcast_ether_addr(dest))
		skb2 = skb;
	else if (is_multicast_ether_addr(dest)) 
        ...
	 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) 
			br_forward(dst->dst, skb, skb2);
		 else
			br_flood_forward(br, skb, skb2);
	
	if (skb2)
		return br_pass_frame_up(skb2);
out:
	return 0;
    ...

如您在上面的br_handle_frame_finish片段中所看到的,

转发数据库中的条目将更新为帧的来源, mac地址,端口,vlan
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
(不在以上代码段中)如果目标地址是多播地址,并且如果禁用了多播,则将丢弃该数据包。否则使用br_multicast_rcv接收到消息
现在,如果混杂模式打开,则无论目的地如何,都将在本地传送数据包。
对于单播地址,我们尝试使用转发数据库(__br_fdb_get)确定端口。
如果目的地是本地,则skb设置为null,即,将不转发数据包。
如果目的地不是本地的,则根据我们是否在转发数据库中找到条目,将帧转发(br_forward)或泛洪到所有端口(br_flood_forward)。
以后,如果需要(根据当前主机是目标主机还是处于混杂模式的网络设备),则在本地传送数据包(br_pass_frame_up)。

br_forward方法可以克隆然后传递(如果也要通过调用liver_clone在本地传递),或者可以通过调用__br_forward将消息直接转发到预期的目标接口。

bt_flood_forward通过遍历br_flood方法中的列表在每个接口上转发帧。
可以查阅代码
https://elixir.free-electrons.com/linux/v3.10.105/source/net/bridge/br_forward.c#L87

使用实例

链接https://linux-blog.anracom.com/2016/02/02/fun-with-veth-devices-linux-virtual-bridges-kvm-vmware-attach-the-host-and-connect-bridges-via-veth/
介绍的比较详细,还有具体的实验详情,可以多多参考。

参考资料
https://wiki.aalto.fi/download/attachments/70789083/linux_bridging_final.pdf
https://linux-blog.anracom.com/2016/02/02/fun-with-veth-devices-linux-virtual-bridges-kvm-vmware-attach-the-host-and-connect-bridges-via-veth/

以上是关于云计算网络之--linux bridge 详解的主要内容,如果未能解决你的问题,请参考以下文章

云计算网络之---tap/tun/veth 虚拟网卡详解

云原生之DockerDocker的网络管理

玩转 OpenStack(八.1)Linux Bridge 实现 Neutron 网络之Local、Flat、VLAN、DHCP

Linux 网络工具详解之 ip tuntap 和 tunctl 创建 tap/tun 设备

linux网络配置之bond-team-bridge

linux下路由设置详解