diff --git a/src/runtime/virtcontainers/network.go b/src/runtime/virtcontainers/network.go index 7f6e74bba8..3ff7ad27db 100644 --- a/src/runtime/virtcontainers/network.go +++ b/src/runtime/virtcontainers/network.go @@ -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 +} diff --git a/src/runtime/virtcontainers/utils/utils.go b/src/runtime/virtcontainers/utils/utils.go index 85c554896a..72dff4380f 100644 --- a/src/runtime/virtcontainers/utils/utils.go +++ b/src/runtime/virtcontainers/utils/utils.go @@ -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 {