mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-19 07:37:07 +00:00
With c75700fe04 we added code to detect
Ubuntu Xenial kernels with a regression in the eBPF subsystem in order
to gently fallback to procfs scanning on such systems (and not crash the
host system by running eBPF code).
With the latest kernel update for Ubuntu Xenial, the bug was fixed:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1763454
Therefore we can update the added check with an upper limit and make
sure that eBPF connection tracking only is disabled on kernels within
the range having the bug.
xref: https://github.com/weaveworks/scope/issues/3131
314 lines
8.0 KiB
Go
314 lines
8.0 KiB
Go
package endpoint
|
|
|
|
import (
|
|
"net"
|
|
"reflect"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/weaveworks/tcptracer-bpf/pkg/tracer"
|
|
|
|
"github.com/weaveworks/scope/probe/host"
|
|
)
|
|
|
|
func newMockEbpfTracker() *EbpfTracker {
|
|
return &EbpfTracker{
|
|
ready: true,
|
|
dead: false,
|
|
|
|
openConnections: map[fourTuple]ebpfConnection{},
|
|
}
|
|
}
|
|
|
|
func TestHandleConnection(t *testing.T) {
|
|
var (
|
|
ServerPid uint32 = 42
|
|
ClientPid uint32 = 43
|
|
ServerIP = net.IP("127.0.0.1")
|
|
ClientIP = net.IP("127.0.0.2")
|
|
ServerPort uint16 = 12345
|
|
ClientPort uint16 = 6789
|
|
NetNS uint32 = 123456789
|
|
|
|
IPv4ConnectEvent = tracer.TcpV4{
|
|
CPU: 0,
|
|
Type: tracer.EventConnect,
|
|
Pid: ClientPid,
|
|
Comm: "cmd",
|
|
SAddr: ClientIP,
|
|
DAddr: ServerIP,
|
|
SPort: ClientPort,
|
|
DPort: ServerPort,
|
|
NetNS: NetNS,
|
|
}
|
|
|
|
IPv4ConnectEbpfConnection = ebpfConnection{
|
|
tuple: fourTuple{
|
|
fromAddr: ClientIP.String(),
|
|
toAddr: ServerIP.String(),
|
|
fromPort: ClientPort,
|
|
toPort: ServerPort,
|
|
},
|
|
networkNamespace: strconv.Itoa(int(NetNS)),
|
|
incoming: false,
|
|
pid: int(ClientPid),
|
|
}
|
|
|
|
IPv4ConnectCloseEvent = tracer.TcpV4{
|
|
CPU: 0,
|
|
Type: tracer.EventClose,
|
|
Pid: ClientPid,
|
|
Comm: "cmd",
|
|
SAddr: ClientIP,
|
|
DAddr: ServerIP,
|
|
SPort: ClientPort,
|
|
DPort: ServerPort,
|
|
NetNS: NetNS,
|
|
}
|
|
|
|
IPv4AcceptEvent = tracer.TcpV4{
|
|
CPU: 0,
|
|
Type: tracer.EventAccept,
|
|
Pid: ServerPid,
|
|
Comm: "cmd",
|
|
SAddr: ServerIP,
|
|
DAddr: ClientIP,
|
|
SPort: ServerPort,
|
|
DPort: ClientPort,
|
|
NetNS: NetNS,
|
|
}
|
|
|
|
IPv4AcceptEbpfConnection = ebpfConnection{
|
|
tuple: fourTuple{
|
|
fromAddr: ServerIP.String(),
|
|
toAddr: ClientIP.String(),
|
|
fromPort: ServerPort,
|
|
toPort: ClientPort,
|
|
},
|
|
networkNamespace: strconv.Itoa(int(NetNS)),
|
|
incoming: true,
|
|
pid: int(ServerPid),
|
|
}
|
|
|
|
IPv4AcceptCloseEvent = tracer.TcpV4{
|
|
CPU: 0,
|
|
Type: tracer.EventClose,
|
|
Pid: ClientPid,
|
|
Comm: "cmd",
|
|
SAddr: ServerIP,
|
|
DAddr: ClientIP,
|
|
SPort: ServerPort,
|
|
DPort: ClientPort,
|
|
NetNS: NetNS,
|
|
}
|
|
)
|
|
|
|
mockEbpfTracker := newMockEbpfTracker()
|
|
|
|
tuple := fourTuple{IPv4ConnectEvent.SAddr.String(), IPv4ConnectEvent.DAddr.String(), uint16(IPv4ConnectEvent.SPort), uint16(IPv4ConnectEvent.DPort)}
|
|
mockEbpfTracker.handleConnection(IPv4ConnectEvent.Type, tuple, int(IPv4ConnectEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectEvent.NetNS), 10))
|
|
if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4ConnectEbpfConnection) {
|
|
t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
|
|
IPv4ConnectEbpfConnection, mockEbpfTracker.openConnections[tuple])
|
|
}
|
|
|
|
tuple = fourTuple{IPv4ConnectCloseEvent.SAddr.String(), IPv4ConnectCloseEvent.DAddr.String(), uint16(IPv4ConnectCloseEvent.SPort), uint16(IPv4ConnectCloseEvent.DPort)}
|
|
mockEbpfTracker.handleConnection(IPv4ConnectCloseEvent.Type, tuple, int(IPv4ConnectCloseEvent.Pid), strconv.FormatUint(uint64(IPv4ConnectCloseEvent.NetNS), 10))
|
|
if len(mockEbpfTracker.openConnections) != 0 {
|
|
t.Errorf("Connection mismatch close event\nConnection to close:%v",
|
|
mockEbpfTracker.openConnections[tuple])
|
|
}
|
|
|
|
mockEbpfTracker = newMockEbpfTracker()
|
|
|
|
tuple = fourTuple{IPv4AcceptEvent.SAddr.String(), IPv4AcceptEvent.DAddr.String(), uint16(IPv4AcceptEvent.SPort), uint16(IPv4AcceptEvent.DPort)}
|
|
mockEbpfTracker.handleConnection(IPv4AcceptEvent.Type, tuple, int(IPv4AcceptEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptEvent.NetNS), 10))
|
|
if !reflect.DeepEqual(mockEbpfTracker.openConnections[tuple], IPv4AcceptEbpfConnection) {
|
|
t.Errorf("Connection mismatch connect event\nTarget connection:%v\nParsed connection:%v",
|
|
IPv4AcceptEbpfConnection, mockEbpfTracker.openConnections[tuple])
|
|
}
|
|
|
|
tuple = fourTuple{IPv4AcceptCloseEvent.SAddr.String(), IPv4AcceptCloseEvent.DAddr.String(), uint16(IPv4AcceptCloseEvent.SPort), uint16(IPv4AcceptCloseEvent.DPort)}
|
|
mockEbpfTracker.handleConnection(IPv4AcceptCloseEvent.Type, tuple, int(IPv4AcceptCloseEvent.Pid), strconv.FormatUint(uint64(IPv4AcceptCloseEvent.NetNS), 10))
|
|
|
|
if len(mockEbpfTracker.openConnections) != 0 {
|
|
t.Errorf("Connection mismatch close event\nConnection to close:%v",
|
|
mockEbpfTracker.openConnections)
|
|
}
|
|
}
|
|
|
|
func TestWalkConnections(t *testing.T) {
|
|
var (
|
|
cnt int
|
|
activeTuple = fourTuple{
|
|
fromAddr: "",
|
|
toAddr: "",
|
|
fromPort: 0,
|
|
toPort: 0,
|
|
}
|
|
|
|
inactiveTuple = fourTuple{
|
|
fromAddr: "",
|
|
toAddr: "",
|
|
fromPort: 0,
|
|
toPort: 0,
|
|
}
|
|
)
|
|
mockEbpfTracker := newMockEbpfTracker()
|
|
mockEbpfTracker.openConnections[activeTuple] = ebpfConnection{
|
|
tuple: activeTuple,
|
|
networkNamespace: "12345",
|
|
incoming: true,
|
|
pid: 0,
|
|
}
|
|
mockEbpfTracker.closedConnections = append(mockEbpfTracker.closedConnections,
|
|
ebpfConnection{
|
|
tuple: inactiveTuple,
|
|
networkNamespace: "12345",
|
|
incoming: false,
|
|
pid: 0,
|
|
})
|
|
mockEbpfTracker.walkConnections(func(e ebpfConnection) {
|
|
cnt++
|
|
})
|
|
if cnt != 2 {
|
|
t.Errorf("walkConnections found %v instead of 2 connections", cnt)
|
|
}
|
|
}
|
|
|
|
func TestInvalidTimeStampDead(t *testing.T) {
|
|
var (
|
|
cnt int
|
|
ClientPid uint32 = 43
|
|
ServerIP = net.IP("127.0.0.1")
|
|
ClientIP = net.IP("127.0.0.2")
|
|
ServerPort uint16 = 12345
|
|
ClientPort uint16 = 6789
|
|
NetNS uint32 = 123456789
|
|
event = tracer.TcpV4{
|
|
CPU: 0,
|
|
Type: tracer.EventConnect,
|
|
Pid: ClientPid,
|
|
Comm: "cmd",
|
|
SAddr: ClientIP,
|
|
DAddr: ServerIP,
|
|
SPort: ClientPort,
|
|
DPort: ServerPort,
|
|
NetNS: NetNS,
|
|
}
|
|
)
|
|
mockEbpfTracker := newMockEbpfTracker()
|
|
event.Timestamp = 0
|
|
mockEbpfTracker.TCPEventV4(event)
|
|
event2 := event
|
|
event2.SPort = 1
|
|
event2.Timestamp = 2
|
|
mockEbpfTracker.TCPEventV4(event2)
|
|
mockEbpfTracker.walkConnections(func(e ebpfConnection) {
|
|
cnt++
|
|
})
|
|
if cnt != 2 {
|
|
t.Errorf("walkConnections found %v instead of 2 connections", cnt)
|
|
}
|
|
if mockEbpfTracker.isDead() {
|
|
t.Errorf("expected ebpfTracker to be alive after events with valid order")
|
|
}
|
|
cnt = 0
|
|
event.Timestamp = 1
|
|
mockEbpfTracker.TCPEventV4(event)
|
|
mockEbpfTracker.walkConnections(func(e ebpfConnection) {
|
|
cnt++
|
|
})
|
|
if cnt != 2 {
|
|
t.Errorf("walkConnections found %v instead of 2 connections", cnt)
|
|
}
|
|
// EbpfTracker is marked as dead asynchronously.
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
if mockEbpfTracker.isDead() {
|
|
break
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
if !mockEbpfTracker.isDead() {
|
|
t.Errorf("expected ebpfTracker to be set to dead after events with wrong order")
|
|
}
|
|
}
|
|
|
|
func TestIsKernelSupported(t *testing.T) {
|
|
var release, version string
|
|
oldGetKernelReleaseAndVersion := host.GetKernelReleaseAndVersion
|
|
defer func() {
|
|
host.GetKernelReleaseAndVersion = oldGetKernelReleaseAndVersion
|
|
}()
|
|
host.GetKernelReleaseAndVersion = func() (string, string, error) { return release, version, nil }
|
|
testVersions := []struct {
|
|
release string
|
|
version string
|
|
supported bool
|
|
}{
|
|
{
|
|
"4.1",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"4.4",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"4.4-custom",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"4.4.127",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"4.4.0-119-generic",
|
|
"#143-Ubuntu SMP Mon Apr 2 16:08:24 UTC 2018",
|
|
false,
|
|
},
|
|
{
|
|
"4.4.0-127-generic",
|
|
"#153-Ubuntu SMP Sat May 19 10:58:46 UTC 2018",
|
|
true,
|
|
},
|
|
{
|
|
"4.4.0-116-generic",
|
|
"#140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018",
|
|
true,
|
|
},
|
|
{
|
|
"4.13.0-38-generic",
|
|
"#43-Ubuntu SMP Wed Mar 14 15:20:44 UTC 2018",
|
|
true,
|
|
},
|
|
{
|
|
"4.13.0-119-generic",
|
|
"#43-Ubuntu SMP Wed Apr 1 00:00:00 UTC 2018",
|
|
true,
|
|
},
|
|
{
|
|
"4.9.0-6-amd64",
|
|
"#1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02)",
|
|
true,
|
|
},
|
|
}
|
|
for _, tv := range testVersions {
|
|
release = tv.release
|
|
version = tv.version
|
|
err := isKernelSupported()
|
|
if tv.supported && err != nil {
|
|
t.Errorf("expected kernel release %q version %q to be supported but got error: %v", release, version, err)
|
|
}
|
|
if !tv.supported && err == nil {
|
|
t.Errorf("expected kernel release %q version %q to not be supported", release, version)
|
|
}
|
|
}
|
|
}
|