云计算网络之--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 详解的主要内容,如果未能解决你的问题,请参考以下文章
玩转 OpenStack(八.1)Linux Bridge 实现 Neutron 网络之Local、Flat、VLAN、DHCP