network: Set queues to 1 to ensure we get the network fds

We want to have the file descriptors of the opened tuntap device to pass
them down to the VMMs, so the VMMs don't have to explicitly open a new
tuntap device themselves, as the `container_kvm_t` label does not allow
such a thing.

With this change we ensure that what's currently done when using QEMU as
the hypervisor, can be easily replicated with other VMMs, even if they
don't support multiqueue.

As a side effect of this, we need to close the received file descriptors
in the code of the VMMs which are not going to use them.

Fixes: #3533

Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@intel.com>
This commit is contained in:
Fabiano Fidêncio 2022-05-19 09:34:08 +02:00
parent 93b61e0f07
commit 0b75522e1f
4 changed files with 50 additions and 19 deletions

View File

@ -1457,25 +1457,22 @@ func (clh *cloudHypervisor) addNet(e Endpoint) error {
mac := e.HardwareAddr()
netPair := e.NetworkPair()
if netPair == nil {
return errors.New("net Pair to be added is nil, needed to get TAP path")
return errors.New("net Pair to be added is nil, needed to get TAP file descriptors")
}
tapPath := netPair.TapInterface.TAPIface.Name
if tapPath == "" {
return errors.New("TAP path in network pair is empty")
if len(netPair.TapInterface.VMFds) == 0 {
return errors.New("The file descriptors for the network pair are not present")
}
clh.netDevicesFiles[mac] = netPair.TapInterface.VMFds
clh.Logger().WithFields(log.Fields{
"mac": mac,
"tap": tapPath,
}).Info("Adding Net")
netRateLimiterConfig := clh.getNetRateLimiterConfig()
net := chclient.NewNetConfig()
net.Mac = &mac
net.Tap = &tapPath
if netRateLimiterConfig != nil {
net.SetRateLimiterConfig(*netRateLimiterConfig)
}

View File

@ -10,6 +10,7 @@ package virtcontainers
import (
"context"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@ -129,25 +130,30 @@ func TestCloudHypervisorAddVSock(t *testing.T) {
// Check addNet appends to the network config list new configurations.
// Check that the elements in the list has the correct values
func TestCloudHypervisorAddNetCheckNetConfigListValues(t *testing.T) {
macTest := "00:00:00:00:00"
tapPath := "/path/to/tap"
assert := assert.New(t)
macTest := "00:00:00:00:00"
file, err := ioutil.TempFile("", "netFd")
assert.Nil(err)
defer os.Remove(file.Name())
vmFds := make([]*os.File, 1)
vmFds = append(vmFds, file)
clh := cloudHypervisor{}
clh.netDevicesFiles = make(map[string][]*os.File)
e := &VethEndpoint{}
e.NetPair.TAPIface.HardAddr = macTest
e.NetPair.TapInterface.TAPIface.Name = tapPath
e.NetPair.TapInterface.VMFds = vmFds
err := clh.addNet(e)
err = clh.addNet(e)
assert.Nil(err)
assert.Equal(len(*clh.netDevices), 1)
if err == nil {
assert.Equal(*(*clh.netDevices)[0].Mac, macTest)
assert.Equal(*(*clh.netDevices)[0].Tap, tapPath)
}
err = clh.addNet(e)
@ -156,7 +162,6 @@ func TestCloudHypervisorAddNetCheckNetConfigListValues(t *testing.T) {
assert.Equal(len(*clh.netDevices), 2)
if err == nil {
assert.Equal(*(*clh.netDevices)[1].Mac, macTest)
assert.Equal(*(*clh.netDevices)[1].Tap, tapPath)
}
}
@ -165,10 +170,18 @@ func TestCloudHypervisorAddNetCheckNetConfigListValues(t *testing.T) {
func TestCloudHypervisorAddNetCheckEnpointTypes(t *testing.T) {
assert := assert.New(t)
tapPath := "/path/to/tap"
macTest := "00:00:00:00:00"
file, err := ioutil.TempFile("", "netFd")
assert.Nil(err)
defer os.Remove(file.Name())
vmFds := make([]*os.File, 1)
vmFds = append(vmFds, file)
validVeth := &VethEndpoint{}
validVeth.NetPair.TapInterface.TAPIface.Name = tapPath
validVeth.NetPair.TAPIface.HardAddr = macTest
validVeth.NetPair.TapInterface.VMFds = vmFds
type args struct {
e Endpoint
@ -189,9 +202,9 @@ func TestCloudHypervisorAddNetCheckEnpointTypes(t *testing.T) {
clh.netDevicesFiles = make(map[string][]*os.File)
if err := clh.addNet(tt.args.e); (err != nil) != tt.wantErr {
t.Errorf("cloudHypervisor.addNet() error = %v, wantErr %v", err, tt.wantErr)
} else if err == nil {
assert.Equal(*(*clh.netDevices)[0].Tap, tapPath)
files := clh.netDevicesFiles[macTest]
assert.Equal(files, vmFds)
}
})
}
@ -201,10 +214,15 @@ func TestCloudHypervisorAddNetCheckEnpointTypes(t *testing.T) {
func TestCloudHypervisorNetRateLimiter(t *testing.T) {
assert := assert.New(t)
tapPath := "/path/to/tap"
file, err := ioutil.TempFile("", "netFd")
assert.Nil(err)
defer os.Remove(file.Name())
vmFds := make([]*os.File, 1)
vmFds = append(vmFds, file)
validVeth := &VethEndpoint{}
validVeth.NetPair.TapInterface.TAPIface.Name = tapPath
validVeth.NetPair.TapInterface.VMFds = vmFds
type args struct {
bwMaxRate int64

View File

@ -927,6 +927,12 @@ func (fc *firecracker) fcAddNetDevice(ctx context.Context, endpoint Endpoint) {
ifaceID := endpoint.Name()
// VMFds are not used by Firecracker, as it opens the tuntap
// device by its name. Let's just close those.
for _, f := range endpoint.NetworkPair().TapInterface.VMFds {
f.Close()
}
// The implementation of rate limiter is based on TBF.
// Rate Limiter defines a token bucket with a maximum capacity (size) to store tokens, and an interval for refilling purposes (refill_time).
// The refill-rate is derived from size and refill_time, and it is the constant rate at which the tokens replenish.

View File

@ -411,6 +411,16 @@ func createLink(netHandle *netlink.Handle, name string, expectedLink netlink.Lin
flags := netlink.TUNTAP_VNET_HDR | netlink.TUNTAP_NO_PI
if queues > 0 {
flags |= netlink.TUNTAP_MULTI_QUEUE_DEFAULTS
} else {
// We need to enforce `queues = 1` here in case
// multi-queue is *not* supported, the reason being
// `linkModify()`, a method called by `LinkAdd()`, only
// returning the file descriptor of the opened tuntap
// device when the queues are set to *non zero*.
//
// Please, for more information, refer to:
// https://github.com/kata-containers/kata-containers/blob/e6e5d2593ac319329269d7b58c30f99ba7b2bf5a/src/runtime/vendor/github.com/vishvananda/netlink/link_linux.go#L1164-L1316
queues = 1
}
newLink = &netlink.Tuntap{
LinkAttrs: netlink.LinkAttrs{Name: name},