rate-limiter: add ifb interface

Ingress traffic shaping is very limited, and the htb
qdisc discipline couldn't be applied to interface ingress traffic.
Here, we import a new pseudo network interface, Intermediate Functional Block (ifb).
It is an alternative to tc filters for handling ingress traffic, by
redirecting interface ingress traffic to ifb and treat it as egress traffic there.

Fixes: #250

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
This commit is contained in:
Penny Zheng 2020-06-10 10:27:58 +00:00
parent cfeb966763
commit 65a37b7d9c
2 changed files with 71 additions and 0 deletions

View File

@ -13,6 +13,7 @@ import (
"math/rand"
"net"
"os"
"os/exec"
"runtime"
"sort"
"time"
@ -1434,3 +1435,51 @@ func addHTBQdisc(linkIndex int, maxRate uint64) error {
return nil
}
// The Intermediate Functional Block (ifb) pseudo network interface is an alternative
// to tc filters for handling ingress traffic,
// By redirecting interface ingress traffic to ifb and treat it as egress traffic there,
// we could do network shaping to interface inbound traffic.
func addIFBDevice() (int, error) {
// check whether host supports ifb
if ok, err := utils.SupportsIfb(); !ok {
return -1, err
}
netHandle, err := netlink.NewHandle()
if err != nil {
return -1, err
}
defer netHandle.Delete()
// There exists error when using netlink library to create ifb interface
cmd := exec.Command("ip", "link", "add", "dev", "ifb0", "type", "ifb")
if output, err := cmd.CombinedOutput(); err != nil {
return -1, fmt.Errorf("Could not create link ifb0: %v, error %v", output, err)
}
ifbLink, err := netlink.LinkByName("ifb0")
if err != nil {
return -1, err
}
if err := netHandle.LinkSetUp(ifbLink); err != nil {
return -1, fmt.Errorf("Could not enable link ifb0 %v", err)
}
return ifbLink.Attrs().Index, nil
}
// This is equivalent to calling:
// tc filter add dev source parent ffff: protocol all u32 match u8 0 0 action mirred egress redirect dev ifb
func addIFBRedirecting(sourceIndex int, ifbIndex int) error {
if err := addQdiscIngress(sourceIndex); err != nil {
return err
}
if err := addRedirectTCFilter(sourceIndex, ifbIndex); err != nil {
return err
}
return nil
}

View File

@ -29,6 +29,9 @@ const MaxSocketPathLen = 107
// VHostVSockDevicePath path to vhost-vsock device
var VHostVSockDevicePath = "/dev/vhost-vsock"
// sysModuleDir is the directory where system modules locate.
var sysModuleDir = "/sys/module"
// FileCopy copys files from srcPath to dstPath
func FileCopy(srcPath, dstPath string) error {
if srcPath == "" {
@ -235,6 +238,25 @@ func SupportsVsocks() bool {
return true
}
// SupportsIfb returns true if ifb are supported, otherwise false
func SupportsIfb() (bool, error) {
ifbModule := "ifb"
// First, check to see if the ifb module is already loaded
path := filepath.Join(sysModuleDir, ifbModule)
if _, err := os.Stat(path); err == nil {
return true, nil
}
// Try to load the ifb module.
// When inserting the ifb module, tell it the number of virtual interfaces you need, here, it's zero.
// The default is 2.
cmd := exec.Command("modprobe", ifbModule, "numifbs=0")
if output, err := cmd.CombinedOutput(); err != nil {
return false, fmt.Errorf("modprobe insert ifb module failed: %s", string(output))
}
return true, nil
}
// StartCmd pointer to a function to start a command.
// Defined this way to allow mock testing.
var StartCmd = func(c *exec.Cmd) error {