09 ifb简介

IFB简介

背景

通过前面几章的介绍,你会发现我们能对上行做到较好的控制,而对下行却基本做不了什么复杂的操作。

这是 tc 所局限的,因为 tc 的初衷就是为了控制本机往外发送的数据,而外部向本机发送的数据 tc 则不是那么关心。正如之前所列出的 tc 原理图一样,你可以很清楚地看到,tc管的是上行:

                Userspace programs
                     ^
                     |
     +---------------+-----------------------------------------+
     |               Y                                         |
     |    -------> IP Stack                                    |
     |   |              |                                      |
     |   |              Y                                      |
     |   |              Y                                      |
     |   ^              |                                      |
     |   |  / ----------> Forwarding ->                        |
     |   ^ /                           |                       |
     |   |/                            Y                       |
     |   |                             |                       |
     |   ^                             Y          /-qdisc1-\   |
     |   |                            Egress     /--qdisc2--\  |
  --->->Ingress                       Classifier ---qdisc3---- | ->
     |   Qdisc                                   \__qdisc4__/  |
     |                                            \-qdiscN_/   |
     |                                                         |
     +----------------------------------------------------------+

仔细地观察,你会发现在 Ingress 处 tc 的结构很简单,而Egress相比于此则要复杂许多。

为了加强对 Ingress 的控制,我们需要使用到 ifb 的功能,使用 ifb 与 tc 配合你可以理解为这样子:

                Userspace programs
                     ^
                     |
     +---------------+-----------------------------------------+
     |               Y                                         |
     |             IP Stack <-------------------------------------------------------|
     |                  |                                      |                    |
     |                  Y                                      |                    |
     |                  Y                                      |                    |
     |                  |                                      |                    |
     |                    Forwarding ->                        |                    |
     |                                 |                       |                    |
     |                                 Y                       |                    |
     |                                 |                       |                    |
     |                                 Y          /-qdisc1-\   |                    |
     |                                Egress     /--qdisc2--\  |                    |
  --->->Ingress                       Classifier ---qdisc3---- | ->                 |
     |   Qdisc                                   \__qdisc4__/  |                    |
     |     |                                      \-qdiscN_/   |                    |
     |     |                                                   |                    |
     +-----|----------------------------------------------------+                   |
           |                    ens33                                               |
           |                                                                        |
           |         +---------------+-----------------------------------------+    |        
           |         |                                                         |    |        
           |         |                                                         |    |        
           |         |                                                         |    |        
           |         |                                                         |    |        
           |         |                                                         |    |        
           |         |                                                         |    |        
           |         |      / ----------> Forwarding ->                        |    |        
           |         |     /                           |                       |    |        
           |         |    /                            Y                       |    |        
           |         |   |                             |                       |    |        
           |         |   ^                             Y          /-qdisc1-\   |    |        
           |         |   |                            Egress     /--qdisc2--\  |    |        
           |--------->->Ingress                       Classifier ---qdisc3---- | ---|
                     |   Qdisc                                   \__qdisc4__/  |
                     |                                            \-qdiscN_/   |
                     |                                                         |
                     +----------------------------------------------------------+
                                            ifb

观察这张图,你就会发现 tc 与 ifb 合作实现对 Ingress 的控制的原理实现,分为三个步骤:

  1. 将 ifb 虚拟网卡拉起来
  2. 将 真实网卡(例如ens33) 的 Ingress 重定向到 ifb 的 Ingress
  3. 对 ifb 的 Engress 进行控制

Hint : 需要注意的是 ifb 的 Egress 重定向到真实网卡(例如 ens33)的 IP Stack 这个过程是 ifb 自己实现的;你也会发现 ifb 的结构比 真实网卡(例如 ens33) 简单,这也是其本身的特性决定的。

只有 ifb 才能办到此功能,其他虚拟网卡的方式是行不通的,例如使用 ifconfig 构造虚拟网卡。ifb之所以行的同是因为其构造出来的网卡具有自己的 MAC 地址,而像 ifconfig 这种方式构造出来的虚拟网卡,MAC 地址是一样的,所以不行。

IFB的原理概述

和tun一样,ifb也是一个虚拟网卡,和tun一样,ifb也是在数据包来自的地方和去往的地方做文章。对于tun而言,数据包在xmit中发往字符设备,而从字符设备写下来的数据包则在tun网卡上模拟一个rx操作,对于ifb而言,情况和这类似。

ifb驱动模拟一块虚拟网卡,它可以被看作是一个只有TC过滤功能的虚拟网卡,说它只有过滤功能,是因为它并不改变数据包的方向,即对于往外发的数据包被重定向到ifb之后,经过ifb的TC过滤之后,依然是通过重定向之前的网卡发出去,对于一个网卡接收的数据包,被重定向到ifb之后,经过ifb的TC过滤之后,依然被重定向之前的网卡继续进行接收处理,不管是从一块网卡发送数据包还是从一块网卡接收数据包,重定向到ifb之后,都要经过一个经由ifb虚拟网卡的dev_queue_xmit操作。

INGRESS队列

Linux TC是一个控发不控收的框架,然而这是对于TC所置于的位置而言的,而不是TC本身的限制,事实上,你完全可以自己在ingress点上实现一个队列机制,说TC控发不控收只是因为Linux TC目前的实现没有实现ingress队列而已。

Linux的协议栈上有诸多的钩子点,Netfilter当然是最显然的了,它不但可以实现防火墙和NAT,也可以将一个数据包在PREROUTING钩子点上queue到一个队列,然后再将此队列的数据包发往一个虚拟网卡,虚拟网卡的xmit回调函数将数据包重新放回Netfilter将数据包STOLEN走的点上,在发往虚拟网卡的时候做发送流控从而变相地实现ingress队列,这就是IMQ的原理,它工作地不错,但是需要在skb中增加字段,使用起来也要牵扯到Netfilter的配置,不是那么纯粹,于是在这个思想的基础上实现了ifb驱动,这个驱动直接挂在TC本身的ingress钩子上,并不和Netfilter发生关系,但是由于TC的钩子机制并没有将一个数据包偷走再放回的机制,于是只有在做完ifb的流控后在ifb网卡的xmit函数中重新调用实际网卡的rx一次,这个实现和Linux Bridge的实现中完成local deliver的实现如出一辙。

QDISC的多网卡共享

除了ingress队列之外,在多个网卡之间共享一个根Qdisc是ifb实现的另一个初衷,可以从文件头的注释中看出来。如果你有10块网卡,想在这10块网卡上实现相同的流控策略,你需要配置10遍吗?将相同的东西抽出来,实现一个ifb虚拟网卡,然后将这10块网卡的流量全部重定向到这个ifb虚拟网卡上,此时只需要在这个虚拟网卡上配置一个Qdisc就可以了。

性能问题

也许你觉得,将多块网卡的流量重定向到一块ifb网卡,这岂不是要将所有的本属于不同的网卡队列被不同CPU处理的数据包排队到ifb虚拟网卡的一个队列被一个CPU处理吗?事实上,这种担心是多余的。

是的,ifb虚拟网卡只有一个网卡接收队列和发送队列,但是这个队列并非被一个CPU处理的,而是被原来处理该数据包的CPU(只是尽量,但不能保证就是原来处理该数据包的那个CPU)继续处理,怎么做到的呢?事实上ifb采用了tasklet来对待数据包的发送和接收,在数据包进入fib的xmit函数之后,将数据包排入队列,然后在本CPU上,注意这个CPU就是原来处理数据包的那个CPU,在本CPU上调度一个tasklet,当tasklet被执行的时候,会取出队列中的数据包进行处理,如果是egress上被重定向到了ifb,就调用原始网卡的xmit,如果是ingress上被重定向到了ifb,就调用原始网卡的rx。当然,tasklet中只是在队列中取出第一个数据包,这个数据包不一定就是在这个CPU上被排入的,这也许会损失一点cache的高利用率带来的性能提升,但不管怎样,如果是多CPU系统,那么显然tasklet不会只在一个CPU上被调度执行。另外,开销还是有一点的,那就是操作单一队列时的自旋锁开销。

优化方式是显然的,那就是将队列实现成“每CPU”的,这样不但可以保证cache的高利用率,也免去了操作单一队列的锁开销。

一个示例配置

# 加载ifb驱动并创建ifb网卡(使用ifconfig -a 如果看到已有则无需该步骤)
modprobe ifb numifbs=1
# up网卡
ip link set dev ifb0 up
 
# 清除原有的根队列(根据实际情况操作,非必要) 
tc qdisc del dev eth0 root 2>/dev/null
tc qdisc del dev eth0 ingress 2>/dev/null
tc qdisc del dev ifb0 root 2>/dev/null
 
#  将eth0的ingress流量全部重定向到 ifb0 处理
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0
 
# eth0的出向限速:eth0添加根队列,使用htb,添加1:1类,使用htb 
tc qdisc add dev eth0 root handle 1: htb r2q 625 default 65
tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit
 
# eth0的入向限速:ifb0添加根队列,使用htb,添加1:1类,使用htb 
tc qdisc add dev ifb0 root handle 1: htb r2q 625 default 65
tc class add dev ifb0 parent 1: classid 1:1 htb rate 1000Mbit
 
# eth0的出向限速:eth0设备添加子类\对应的filter配置规则和子类的队列
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10Mbit
tc filter add dev eth0 parent 1: protocol all prio 1 u32 match ip dst 192.168.0.2 classid 1:10
tc qdisc add dev eth0 parent 1:10 handle 10: sfq
 
# eth0的出向限速:eth0设备添加子类\对应的filter配置规则和子类的队列 
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 20Mbit
tc filter add dev eth0 parent 1: protocol all prio 1 u32 match ip dst 192.168.0.3 classid 1:11
tc qdisc add dev eth0 parent 1:11 handle 11: sfq
 
 
# eth0的入向限速:ifb0设备添加子类\对应的filter配置规则和子类的队列
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 10Mbit
tc filter add dev ifb0 parent 1: protocol all prio 1 u32 match ip src 192.168.0.2 classid 1:10
tc qdisc add dev ifb0 parent 1:10 handle 10: sfq
 
 
# eth0的入向限速:ifb0设备添加子类\对应的filter配置规则和子类的队列 
tc class add dev ifb0 parent 1:1 classid 1:11 htb rate 20Mbit
tc filter add dev ifb0 parent 1: protocol all prio 1 u32 match ip src 192.168.0.3 classid 1:11
tc qdisc add dev ifb0 parent 1:11 handle 11: sfq

开启 ifb

正如tc所需要的模块支持,ifb也是需要对应的模块支持的。

以下是 Linux 内核编译文件 Kconfig 中 IFB 模块的原文:

config IFB
 tristate "Intermediate Functional Block support"
 depends on NET_CLS_ACT
 ---help---
   This is an intermediate driver that allows sharing of
   resources.
   To compile this driver as a module, choose M here: the module
   will be called ifb.  If you want to use more than one ifb
   device at a time, you need to compile this driver as a module.
   Instead of 'ifb', the devices will then be called 'ifb0',
   'ifb1' etc.
   Look at the iproute2 documentation directory for usage etc

可以看到IFB依赖于其他模块 NET_CLS_ACT,如果编译失败的话很可能是 NET_CLS_ACT 没有开启。(但是一般是不会的,因为开启一个模块,对应它所依赖的模块也会被开启,而且此过程是递归的。)

翻到最上面,看到以下内容:

menuconfig NETDEVICES
 default y if UML
 depends on NET
 bool "Network device support"

记录下这个名字,它在上层应该是被设置成。

但是在 make menuconfig 中并没有发现 Network device support,而且在当前文件没有找到包含 Network device support 的menu,所以这个文件肯定被别人包含走了;于是确认当前文件名为 “/drivers/net/Kconfig”,然后全文检索 “source “drivers/net/Kconfig””,找到包含此句的文件名为 “/drivers/Kconfig”, 然后在此文件查找 menu 可以找到如下内容:

menu "Device Drivers"

所以得出结论,在 make menuconfig 中,IFB模块的位置为:

Device Drivers
    -> Network device support
        -> Intermediate Functional Block support

按照这里路径,在 make menuconfig 中看到以下画面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dXo3eNdu-1635818973119)(./images/09_ifb简介_make_menuconfig.png)]
可以看到 IFB 是没有开启的,将它开启即可。

参考

LINUX使用TC并借助IFB实现入向限速(内附配置实例)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值