Files
kata-containers/netmon/netmon_test.go
Archana Shinde 8abd2ec53f netmon: Fix bug in how routes are converted
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>
2019-04-12 09:42:47 -07:00

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)
}