mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-06 16:04:26 +01:00
The agent expects a IP CIDR for the route destination rather than an IP address. netmon was incorrectly converting route dest to an IP address and hence exiting with an error. We did not have an integration test for netmon with tcfilter mode. macvtap mode did not uncover this, as with macvtap routes are not really passed to the agent. We delete the IP on the veth device, and netmon looks at the routes after the IP is deleted with macvtap. Fixes #1523 Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
638 lines
14 KiB
Go
638 lines
14 KiB
Go
// Copyright (c) 2018 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"testing"
|
|
|
|
vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/vishvananda/netns"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const (
|
|
testSandboxID = "123456789"
|
|
testRuntimePath = "/foo/bar/test-runtime"
|
|
testLogLevel = "info"
|
|
testStorageParentPath = "/tmp/netmon"
|
|
testSharedFile = "foo-shared.json"
|
|
testWrongNetlinkFamily = -1
|
|
testIfaceName = "test_eth0"
|
|
testMTU = 12345
|
|
testHwAddr = "02:00:ca:fe:00:48"
|
|
testIPAddress = "192.168.0.15"
|
|
testIPAddressWithMask = "192.168.0.15/32"
|
|
testScope = 1
|
|
testTxQLen = -1
|
|
testIfaceIndex = 5
|
|
)
|
|
|
|
func skipUnlessRoot(t *testing.T) {
|
|
if os.Getuid() != 0 {
|
|
t.Skip("Test disabled as requires root user")
|
|
}
|
|
}
|
|
|
|
func TestNewNetmon(t *testing.T) {
|
|
skipUnlessRoot(t)
|
|
|
|
// Override storageParentPath
|
|
savedStorageParentPath := storageParentPath
|
|
storageParentPath = testStorageParentPath
|
|
defer func() {
|
|
storageParentPath = savedStorageParentPath
|
|
}()
|
|
|
|
params := netmonParams{
|
|
sandboxID: testSandboxID,
|
|
runtimePath: testRuntimePath,
|
|
debug: true,
|
|
logLevel: testLogLevel,
|
|
}
|
|
|
|
expected := &netmon{
|
|
netmonParams: params,
|
|
storagePath: filepath.Join(storageParentPath, params.sandboxID),
|
|
sharedFile: filepath.Join(storageParentPath, params.sandboxID, sharedFile),
|
|
}
|
|
|
|
os.RemoveAll(expected.storagePath)
|
|
|
|
got, err := newNetmon(params)
|
|
assert.Nil(t, err)
|
|
assert.True(t, reflect.DeepEqual(expected.netmonParams, got.netmonParams),
|
|
"Got %+v\nExpected %+v", got.netmonParams, expected.netmonParams)
|
|
assert.True(t, reflect.DeepEqual(expected.storagePath, got.storagePath),
|
|
"Got %+v\nExpected %+v", got.storagePath, expected.storagePath)
|
|
assert.True(t, reflect.DeepEqual(expected.sharedFile, got.sharedFile),
|
|
"Got %+v\nExpected %+v", got.sharedFile, expected.sharedFile)
|
|
|
|
_, err = os.Stat(got.storagePath)
|
|
assert.Nil(t, err)
|
|
|
|
os.RemoveAll(got.storagePath)
|
|
}
|
|
|
|
func TestNewNetmonErrorWrongFamilyType(t *testing.T) {
|
|
// Override netlinkFamily
|
|
savedNetlinkFamily := netlinkFamily
|
|
netlinkFamily = testWrongNetlinkFamily
|
|
defer func() {
|
|
netlinkFamily = savedNetlinkFamily
|
|
}()
|
|
|
|
n, err := newNetmon(netmonParams{})
|
|
assert.NotNil(t, err)
|
|
assert.Nil(t, n)
|
|
}
|
|
|
|
func TestCleanup(t *testing.T) {
|
|
skipUnlessRoot(t)
|
|
|
|
// Override storageParentPath
|
|
savedStorageParentPath := storageParentPath
|
|
storageParentPath = testStorageParentPath
|
|
defer func() {
|
|
storageParentPath = savedStorageParentPath
|
|
}()
|
|
|
|
handler, err := netlink.NewHandle(netlinkFamily)
|
|
assert.Nil(t, err)
|
|
|
|
n := &netmon{
|
|
storagePath: filepath.Join(storageParentPath, testSandboxID),
|
|
linkDoneCh: make(chan struct{}),
|
|
rtDoneCh: make(chan struct{}),
|
|
netHandler: handler,
|
|
}
|
|
|
|
err = os.MkdirAll(n.storagePath, storageDirPerm)
|
|
assert.Nil(t, err)
|
|
_, err = os.Stat(n.storagePath)
|
|
assert.Nil(t, err)
|
|
|
|
n.cleanup()
|
|
|
|
_, err = os.Stat(n.storagePath)
|
|
assert.NotNil(t, err)
|
|
_, ok := (<-n.linkDoneCh)
|
|
assert.False(t, ok)
|
|
_, ok = (<-n.rtDoneCh)
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestLogger(t *testing.T) {
|
|
fields := logrus.Fields{
|
|
"name": netmonName,
|
|
"pid": os.Getpid(),
|
|
"source": "netmon",
|
|
"sandbox": testSandboxID,
|
|
}
|
|
|
|
expected := netmonLog.WithFields(fields)
|
|
|
|
n := &netmon{
|
|
netmonParams: netmonParams{
|
|
sandboxID: testSandboxID,
|
|
},
|
|
}
|
|
|
|
got := n.logger()
|
|
assert.True(t, reflect.DeepEqual(*expected, *got),
|
|
"Got %+v\nExpected %+v", *got, *expected)
|
|
}
|
|
|
|
func TestConvertInterface(t *testing.T) {
|
|
hwAddr, err := net.ParseMAC(testHwAddr)
|
|
assert.Nil(t, err)
|
|
|
|
addrs := []netlink.Addr{
|
|
{
|
|
IPNet: &net.IPNet{
|
|
IP: net.ParseIP(testIPAddress),
|
|
},
|
|
},
|
|
}
|
|
|
|
linkAttrs := &netlink.LinkAttrs{
|
|
Name: testIfaceName,
|
|
MTU: testMTU,
|
|
HardwareAddr: hwAddr,
|
|
}
|
|
|
|
linkType := "link_type_test"
|
|
|
|
expected := vcTypes.Interface{
|
|
Device: testIfaceName,
|
|
Name: testIfaceName,
|
|
Mtu: uint64(testMTU),
|
|
HwAddr: testHwAddr,
|
|
IPAddresses: []*vcTypes.IPAddress{
|
|
{
|
|
Family: netlinkFamily,
|
|
Address: testIPAddress,
|
|
Mask: "0",
|
|
},
|
|
},
|
|
LinkType: linkType,
|
|
}
|
|
|
|
got := convertInterface(linkAttrs, linkType, addrs)
|
|
assert.True(t, reflect.DeepEqual(expected, got),
|
|
"Got %+v\nExpected %+v", got, expected)
|
|
}
|
|
|
|
func TestConvertRoutes(t *testing.T) {
|
|
ip, ipNet, err := net.ParseCIDR(testIPAddressWithMask)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, ipNet)
|
|
|
|
routes := []netlink.Route{
|
|
{
|
|
Dst: ipNet,
|
|
Src: ip,
|
|
Gw: ip,
|
|
LinkIndex: -1,
|
|
Scope: testScope,
|
|
},
|
|
}
|
|
|
|
expected := []vcTypes.Route{
|
|
{
|
|
Dest: testIPAddressWithMask,
|
|
Gateway: testIPAddress,
|
|
Source: testIPAddress,
|
|
Scope: uint32(testScope),
|
|
},
|
|
}
|
|
|
|
got := convertRoutes(routes)
|
|
assert.True(t, reflect.DeepEqual(expected, got),
|
|
"Got %+v\nExpected %+v", got, expected)
|
|
}
|
|
|
|
type testTeardownNetwork func()
|
|
|
|
func testSetupNetwork(t *testing.T) testTeardownNetwork {
|
|
skipUnlessRoot(t)
|
|
|
|
// new temporary namespace so we don't pollute the host
|
|
// lock thread since the namespace is thread local
|
|
runtime.LockOSThread()
|
|
var err error
|
|
ns, err := netns.New()
|
|
if err != nil {
|
|
t.Fatal("Failed to create newns", ns)
|
|
}
|
|
|
|
return func() {
|
|
ns.Close()
|
|
runtime.UnlockOSThread()
|
|
}
|
|
}
|
|
|
|
func testCreateDummyNetwork(t *testing.T, handler *netlink.Handle) (int, vcTypes.Interface) {
|
|
hwAddr, err := net.ParseMAC(testHwAddr)
|
|
assert.Nil(t, err)
|
|
|
|
link := &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
MTU: testMTU,
|
|
TxQLen: testTxQLen,
|
|
Name: testIfaceName,
|
|
HardwareAddr: hwAddr,
|
|
},
|
|
}
|
|
|
|
err = handler.LinkAdd(link)
|
|
assert.Nil(t, err)
|
|
err = handler.LinkSetUp(link)
|
|
assert.Nil(t, err)
|
|
|
|
attrs := link.Attrs()
|
|
assert.NotNil(t, attrs)
|
|
|
|
iface := vcTypes.Interface{
|
|
Device: testIfaceName,
|
|
Name: testIfaceName,
|
|
Mtu: uint64(testMTU),
|
|
HwAddr: testHwAddr,
|
|
LinkType: link.Type(),
|
|
}
|
|
|
|
return attrs.Index, iface
|
|
}
|
|
|
|
func TestScanNetwork(t *testing.T) {
|
|
tearDownNetworkCb := testSetupNetwork(t)
|
|
defer tearDownNetworkCb()
|
|
|
|
handler, err := netlink.NewHandle(netlinkFamily)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, handler)
|
|
defer handler.Delete()
|
|
|
|
idx, expected := testCreateDummyNetwork(t, handler)
|
|
|
|
n := &netmon{
|
|
netIfaces: make(map[int]vcTypes.Interface),
|
|
netHandler: handler,
|
|
}
|
|
|
|
err = n.scanNetwork()
|
|
assert.Nil(t, err)
|
|
assert.True(t, reflect.DeepEqual(expected, n.netIfaces[idx]),
|
|
"Got %+v\nExpected %+v", n.netIfaces[idx], expected)
|
|
}
|
|
|
|
func TestStoreDataToSend(t *testing.T) {
|
|
var got vcTypes.Interface
|
|
|
|
expected := vcTypes.Interface{
|
|
Device: testIfaceName,
|
|
Name: testIfaceName,
|
|
Mtu: uint64(testMTU),
|
|
HwAddr: testHwAddr,
|
|
}
|
|
|
|
n := &netmon{
|
|
sharedFile: filepath.Join(testStorageParentPath, testSharedFile),
|
|
}
|
|
|
|
err := os.MkdirAll(testStorageParentPath, storageDirPerm)
|
|
defer os.RemoveAll(testStorageParentPath)
|
|
assert.Nil(t, err)
|
|
|
|
err = n.storeDataToSend(expected)
|
|
assert.Nil(t, err)
|
|
|
|
// Check the file has been created, check the content, and delete it.
|
|
_, err = os.Stat(n.sharedFile)
|
|
assert.Nil(t, err)
|
|
byteArray, err := ioutil.ReadFile(n.sharedFile)
|
|
assert.Nil(t, err)
|
|
err = json.Unmarshal(byteArray, &got)
|
|
assert.Nil(t, err)
|
|
assert.True(t, reflect.DeepEqual(expected, got),
|
|
"Got %+v\nExpected %+v", got, expected)
|
|
}
|
|
|
|
func TestExecKataCmdSuccess(t *testing.T) {
|
|
trueBinPath, err := exec.LookPath("true")
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, trueBinPath)
|
|
|
|
params := netmonParams{
|
|
runtimePath: trueBinPath,
|
|
}
|
|
|
|
n := &netmon{
|
|
netmonParams: params,
|
|
sharedFile: filepath.Join(testStorageParentPath, testSharedFile),
|
|
}
|
|
|
|
err = os.MkdirAll(testStorageParentPath, storageDirPerm)
|
|
assert.Nil(t, err)
|
|
defer os.RemoveAll(testStorageParentPath)
|
|
|
|
file, err := os.Create(n.sharedFile)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, file)
|
|
file.Close()
|
|
|
|
_, err = os.Stat(n.sharedFile)
|
|
assert.Nil(t, err)
|
|
|
|
err = n.execKataCmd("")
|
|
assert.Nil(t, err)
|
|
_, err = os.Stat(n.sharedFile)
|
|
assert.NotNil(t, err)
|
|
}
|
|
|
|
func TestExecKataCmdFailure(t *testing.T) {
|
|
falseBinPath, err := exec.LookPath("false")
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, falseBinPath)
|
|
|
|
params := netmonParams{
|
|
runtimePath: falseBinPath,
|
|
}
|
|
|
|
n := &netmon{
|
|
netmonParams: params,
|
|
}
|
|
|
|
err = n.execKataCmd("")
|
|
assert.NotNil(t, err)
|
|
}
|
|
|
|
func TestActionsCLI(t *testing.T) {
|
|
trueBinPath, err := exec.LookPath("true")
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, trueBinPath)
|
|
|
|
params := netmonParams{
|
|
runtimePath: trueBinPath,
|
|
}
|
|
|
|
n := &netmon{
|
|
netmonParams: params,
|
|
sharedFile: filepath.Join(testStorageParentPath, testSharedFile),
|
|
}
|
|
|
|
err = os.MkdirAll(testStorageParentPath, storageDirPerm)
|
|
assert.Nil(t, err)
|
|
defer os.RemoveAll(testStorageParentPath)
|
|
|
|
// Test addInterfaceCLI
|
|
err = n.addInterfaceCLI(vcTypes.Interface{})
|
|
assert.Nil(t, err)
|
|
|
|
// Test delInterfaceCLI
|
|
err = n.delInterfaceCLI(vcTypes.Interface{})
|
|
assert.Nil(t, err)
|
|
|
|
// Test updateRoutesCLI
|
|
err = n.updateRoutesCLI([]vcTypes.Route{})
|
|
assert.Nil(t, err)
|
|
|
|
tearDownNetworkCb := testSetupNetwork(t)
|
|
defer tearDownNetworkCb()
|
|
|
|
handler, err := netlink.NewHandle(netlinkFamily)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, handler)
|
|
defer handler.Delete()
|
|
|
|
n.netHandler = handler
|
|
|
|
// Test updateRoutes
|
|
err = n.updateRoutes()
|
|
assert.Nil(t, err)
|
|
|
|
// Test handleRTMDelRoute
|
|
err = n.handleRTMDelRoute(netlink.RouteUpdate{})
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleRTMNewAddr(t *testing.T) {
|
|
n := &netmon{}
|
|
|
|
err := n.handleRTMNewAddr(netlink.LinkUpdate{})
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleRTMDelAddr(t *testing.T) {
|
|
n := &netmon{}
|
|
|
|
err := n.handleRTMDelAddr(netlink.LinkUpdate{})
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleRTMNewLink(t *testing.T) {
|
|
n := &netmon{}
|
|
ev := netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{},
|
|
}
|
|
|
|
// LinkAttrs is nil
|
|
err := n.handleRTMNewLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Link name contains "kata" suffix
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo_kata",
|
|
},
|
|
},
|
|
}
|
|
err = n.handleRTMNewLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Interface already exist in list
|
|
n.netIfaces = make(map[int]vcTypes.Interface)
|
|
n.netIfaces[testIfaceIndex] = vcTypes.Interface{}
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo0",
|
|
},
|
|
},
|
|
}
|
|
ev.Index = testIfaceIndex
|
|
err = n.handleRTMNewLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Flags are not up and running
|
|
n.netIfaces = make(map[int]vcTypes.Interface)
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo0",
|
|
},
|
|
},
|
|
}
|
|
ev.Index = testIfaceIndex
|
|
err = n.handleRTMNewLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Invalid link
|
|
n.netIfaces = make(map[int]vcTypes.Interface)
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo0",
|
|
},
|
|
},
|
|
}
|
|
ev.Index = testIfaceIndex
|
|
ev.Flags = unix.IFF_UP | unix.IFF_RUNNING
|
|
handler, err := netlink.NewHandle(netlinkFamily)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, handler)
|
|
defer handler.Delete()
|
|
n.netHandler = handler
|
|
err = n.handleRTMNewLink(ev)
|
|
assert.NotNil(t, err)
|
|
}
|
|
|
|
func TestHandleRTMDelLink(t *testing.T) {
|
|
n := &netmon{}
|
|
ev := netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{},
|
|
}
|
|
|
|
// LinkAttrs is nil
|
|
err := n.handleRTMDelLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Link name contains "kata" suffix
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo_kata",
|
|
},
|
|
},
|
|
}
|
|
err = n.handleRTMDelLink(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// Interface does not exist in list
|
|
n.netIfaces = make(map[int]vcTypes.Interface)
|
|
ev = netlink.LinkUpdate{
|
|
Link: &netlink.Dummy{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: "foo0",
|
|
},
|
|
},
|
|
}
|
|
ev.Index = testIfaceIndex
|
|
err = n.handleRTMDelLink(ev)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleRTMNewRouteIfaceNotFound(t *testing.T) {
|
|
n := &netmon{
|
|
netIfaces: make(map[int]vcTypes.Interface),
|
|
}
|
|
|
|
err := n.handleRTMNewRoute(netlink.RouteUpdate{})
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleLinkEvent(t *testing.T) {
|
|
n := &netmon{}
|
|
ev := netlink.LinkUpdate{}
|
|
|
|
// Unknown event
|
|
err := n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// DONE event
|
|
ev.Header.Type = unix.NLMSG_DONE
|
|
err = n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// ERROR event
|
|
ev.Header.Type = unix.NLMSG_ERROR
|
|
err = n.handleLinkEvent(ev)
|
|
assert.NotNil(t, err)
|
|
|
|
// NEWADDR event
|
|
ev.Header.Type = unix.RTM_NEWADDR
|
|
err = n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// DELADDR event
|
|
ev.Header.Type = unix.RTM_DELADDR
|
|
err = n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// NEWLINK event
|
|
ev.Header.Type = unix.RTM_NEWLINK
|
|
ev.Link = &netlink.Dummy{}
|
|
err = n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// DELLINK event
|
|
ev.Header.Type = unix.RTM_DELLINK
|
|
ev.Link = &netlink.Dummy{}
|
|
err = n.handleLinkEvent(ev)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestHandleRouteEvent(t *testing.T) {
|
|
n := &netmon{}
|
|
ev := netlink.RouteUpdate{}
|
|
|
|
// Unknown event
|
|
err := n.handleRouteEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
// RTM_NEWROUTE event
|
|
ev.Type = unix.RTM_NEWROUTE
|
|
err = n.handleRouteEvent(ev)
|
|
assert.Nil(t, err)
|
|
|
|
trueBinPath, err := exec.LookPath("true")
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, trueBinPath)
|
|
|
|
n.runtimePath = trueBinPath
|
|
n.sharedFile = filepath.Join(testStorageParentPath, testSharedFile)
|
|
|
|
err = os.MkdirAll(testStorageParentPath, storageDirPerm)
|
|
assert.Nil(t, err)
|
|
defer os.RemoveAll(testStorageParentPath)
|
|
|
|
tearDownNetworkCb := testSetupNetwork(t)
|
|
defer tearDownNetworkCb()
|
|
|
|
handler, err := netlink.NewHandle(netlinkFamily)
|
|
assert.Nil(t, err)
|
|
assert.NotNil(t, handler)
|
|
defer handler.Delete()
|
|
|
|
n.netHandler = handler
|
|
|
|
// RTM_DELROUTE event
|
|
ev.Type = unix.RTM_DELROUTE
|
|
err = n.handleRouteEvent(ev)
|
|
assert.Nil(t, err)
|
|
}
|