Linux内核Bridge网桥功能学习笔记

Linux内核Bridge网桥功能学习笔记

  • 1. 背景介绍

    在前两篇文章中,我们学习了Linux内核switchdev框架和实现代码,其中提到switchdev驱动的很大一部分是卸载内核中bridge网桥功能到交换芯片设备中。那么内核中的网桥功能具体是指什么,他和我们平常说的交换机又有什么区别,网桥有哪些应用,带着这些问题我研究了一下内核中的Bridge模块,当实际深入之后,发现它并没有想象的那么简单。

    • 2. 网桥功能实战

    网桥是什么,让我们通过在Linux主机上的一个实验来体会一下网桥的功能。

    聊聊 Linux 上软件实现的“交换机” – Bridge!这篇文章中,作者通过在Linux上创建一个小型的虚拟网络,来演示了网桥如何将不同的网络连接起来的。直接按照作者的配置命令就可以,但是在这个过程中我有了一些细微的调整,所以还是把我的完整命令记录下来,以便自己以后查阅。

    网络拓扑图如下:

    图片来自https://mp.weixin.qq.com/s/JnKz1fUgZmGdvfxOm2ehZg

    配置命令如下:

    # 配置netns1及相关veth接口
    
    # 创建网络命名空间netns1
    sudo ip netns add netns1
    
    # 创建veth口veth1和veth1_p
    sudo ip link add veth1 type veth peer name veth1_p
    
    # 将veth1加入到netns1中
    sudo ip link set veth1 netns netns1
    
    # 为veth1配置ip地址
    sudo ip netns exec netns1 ip addr add 192.168.0.101/24 dev veth1
    
    # 将veth1口设置为up状态
    sudo ip netns exec netns1 ip link set veth1 up
    
    # 查看netns1下的link状态
    sudo ip netns exec netns1 ip link list
    
    # 查看netns1下的接口信息
    sudo ip netns exec netns1 ifconfig
    
    # 配置netns2及相关veth接口
    
    # 创建网络命名空间netns2
    sudo ip netns add netns2
    
    # 创建veth口veth2和veth2_p
    sudo ip link add veth2 type veth peer name veth2_p
    
    # 将veth2加入到netns2中
    sudo ip link set veth2 netns netns2
    
    # 为veth2配置ip地址
    sudo ip netns exec netns2 ip addr add 192.168.0.102/24 dev veth2
    
    # 将veth2口设置为up状态
    sudo ip netns exec netns2 ip link set veth2 up
    
    # 查看netns2下的link状态
    sudo ip netns exec netns2 ip link list
    
    # 查看netns2下的接口信息
    sudo ip netns exec netns2 ifconfig
    
    # 配置bridge相关
    
    # 创建bridge并命名为br0
    sudo ip link add br0 type bridge
    
    # 查看br0状态
    ip link show br0
    
    # 将vet1_p加入br0
    sudo ip link set dev veth1_p master br0
    # 将vet2_p加入br0
    sudo ip link set dev veth2_p master br0
    
    # 为br0配置ip地址
    sudo ip addr add 192.168.0.100/24 dev br0
    
    # 将bridge和veth设置为up,这样可以正常转发报文
    
    # 将veth1_p设置为up状态
    sudo ip link set veth1_p up
    
    # 将veth2_p设置为up状态
    sudo ip link set veth2_p up
    
    # 将br0设置为up状态
    sudo ip link set br0 up
    
    # 查看bridge相关状态
    
    # 显示br0状态
    ip link show br0
    
    # 显示br0状态
    ip link show master br0
    
    # 显示bridge统计信息
    bridge -s link
    
    # 显示brdige详细信息
    bridge -d link
    
    # 显示br0地址信息
    ip addr show br0

    完成上述操作后,就可以通过如下ping命令测试网络是否连通了。

    thinkdancer@mypc:~$ sudo ip netns exec netns1 ping 192.168.0.102 -I veth1
    PING 192.168.0.102 (192.168.0.102) from 192.168.0.101 veth1: 56(84) bytes of data.
    64 bytes from 192.168.0.102: icmp_seq=1 ttl=64 time=0.058 ms
    64 bytes from 192.168.0.102: icmp_seq=2 ttl=64 time=0.042 ms
    64 bytes from 192.168.0.102: icmp_seq=3 ttl=64 time=0.042 ms
    

    在以上实验操作中有几点需要注意:

    1. bridge和veth都要手动up,因为是虚拟设备,所以不会像物理设备那样连上网线自动up,不设置会导致报文不能正常转发,最终无法ping通,本文在配置的时候就漏掉了将br0设备配置为up,导致ping失败。
    2. 上面的配置和原文的主要差异是,废除了原文使用的brctl命令,而全部以ip或者bridge命令替代,这样整个配置过程使用的都是iproute2工具。
    3. ip link show master br0ip link show br0两条命令的差异是前者多了一个master关键字,输出结果中可以看到br0包含的接口列表,而后者只能看到br0自己的接口状态,所以前者的输出结果更接近于brctl show的输出。
    4. ip命令的关键字很多,在使用的时候除了借助于搜索引擎外,还可以直接输入ip link help,ip addr help或者ip netns help查看更加详细的关键字说明。另外man ip link还可以看到ip link命令的关键字解释,man ip netns也可以看到ip netns命令关键字的解释,但是man ip addr就只能看到ip命令关键字的解释,效果和man ip是一样的。
    • 3. 网桥功能原理分析

    3.1 网桥模型

    由于历史的原因,Linux内核中将网络设备中的交换机称为网桥Bridge,其实含义是一样的,就是实现不同接口之间的L2层报文转发。这里的接口可以是具有物理网卡的网口,也可以是veth, tun/tap等虚拟接口,而后者在有些场景具有更强大的连接功能。

    网桥还具有三层交换机的功能,即网桥本身可以有L3层的转发能力。通过在bridge对应的接口上配置IP地址,可以实现L3层面的转发,这样可以将bridge和外部网络世界连通起来,而不仅仅是一个L2转发域。

    当然,相对于传统交换机上的access, trunk等模型,bridge上并没有直接对应的概念,但是通过一些方法和技巧可以实现类似的效果。在这里我们主要关注bridge的主要功能,而不仅仅是传统物理交换机做对比,看看它在具体实践中的作用。

    3.2 网桥报文收发包

    对于Linux来说,大部分网络接口都是L3模式,也就是根据报文的IP地址查找路由表做转发,比如我们普通的物理网卡就是这种模式。当需要将接口加入网桥的时候,实际是将接口设置为L2模式,直接处理L2层报文。在内核中是在接口加入网桥的时候,通过在br_add_if中使用netdev_rx_handler_register注册网桥的处理函数br_handle_frame来实现的,br_handle_frame中会根据报文格式以及查找结果决定报文的处理结果,如果是需要L2处理的报文,比如转发到另一个接口或者上送bridge对应的L3口处理,则返回RX_HANDLER_CONSUMED,如果是不需要处理的表文,比如一些组播协议LLDP,则返回RX_HANDLER_PASS交由协议层继续处理。上面RX_HANDLER_CONSUMED的含义是已经被消耗,所以直接跳出了网络层核心处理函数__netif_receive_skb_core,也就意味着不被后续的ptype_base注册对应流程处理,比如0x0800对应的IPv4或者0x86DD对应的IPv6,但是因为在ptype_all之前,所以tcpdump还是可以正常抓取到报文。

    br_handle_frame中在判断接口状态是fowarding或者learning的时候,会调用nf_hook_bridge_pre,而后者最终会调用br_handle_frame_finish处理报文,注意这里的forwarding是接口的状态,而不是报文的处理结果。在br_handle_frame_finish中,根据报文是组播还是单播查找对应的转发表,如果dst存在且flags中的BR_FDB_LOCAL置位,则表示是需要上送更高一层的协议栈,比如IP层,则交给br_pass_frame_up处理,如果dst存在但BR_FDB_LOCAL未置位,则表示需要做L2转发的表文,则交给br_forward处理。如果dst不存在则通过br_flood或者br_multicast_flood泛洪报文。br_pass_frame_up最终会通过netif_receive_skb交给上层协议栈继续处理。

    3.3 网桥FDB

    从上面的收发包流程可以看出,每个报文都需要通过查找FDB表来决定下一步的处理流程,而在查找的过程中也会同时学习报文的SMAC到FDB转发表中,这一点和传统的交换机是一样的,这个流程是在br_handle_frame_finish中调用br_fdb_update实现的。

    fdb是brdige中的核心处理模块,涉及fdb数据结构维护,entry表项的添加和删除,静态表项的维护,动态表项的老化,fdb通过switchdev机制到硬件芯片中的卸载和反向通知机制。这部分和route中的fib其实是一样的思想,就是处理核心的转发表项和对应的接口资源信息。这里查找使用的key是:addr + vid,如果接口没有vlan,则vid = 1,否则使用接口或者报文的vlan。在进行key的查找之前,还会通过br_allowed_ingress进行入方向的报文过滤功能,在这里会根据接口允许的vlan以及报文是否有vlan来决定后续查找key中的vid是多少。当在fdb中查找到dst后会通过fdb_forward转发报文,而在这其中会通过br_handle_vlan对出方向的报文vlan做处理,以便支持tag/untag等不同的应用场景。

    3.4 网桥VLAN

    在传统交换机中,VLAN是用来隔离广播域的,同时在传统交换机中还有access, trunk的概念,用来将多个VLAN的聚合在一起。在bridge中也不例外,但是bridge中的vlan和口有关,一个bridge中可以包含多个VLAN口。在bridge的创建中你其实是看不到VLAN的,只能看到添加或者删除了哪些接口。当接口收到带有VLAN字段的报文时,会先经过VLAN模块处理,然后再走bridge转发流程,也就是说可以将eth0.100这样的接口加入到bridge中,效果是将VLAN剥掉后走L3转发或者走L2转发,具体哪种取决于报文的DMAC是不是本机的MAC地址。如果是L3转发则根据剥掉VLAN后的报文查找路由表,如果是L2转发则根据VLAN+DMAC查找FDB表。

    参考资料

    1. 聊聊 Linux 上软件实现的“交换机” – Bridge!
    2. IpRoute2 简明教程
    3. 配置网络桥接
    4. Linux 网络虚拟化技术(三)bridge 虚拟设备
    5. Ethernet Bridging
    6. Linux网桥实现分析 
    7. Bridge框架 – awokezhou/LinuxPage GitHub Wiki
    Comments are closed.