ebpf

package
v0.0.0-...-dca2ff9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 10, 2020 License: Apache-2.0, Apache-2.0 Imports: 23 Imported by: 0

README

Note

This package is a fork of the weaveworks tcptracer-bpf package which focused on tracing TCP state events (connect, accept, close) without kernel specific runtime dependencies.

This fork adds support for UDP, as well as collection of metrics like bytes sent/received. It also opts for event collection via polling (using BPF maps) instead of being pushed event updates via perf buffers.

tracer-bpf

tracer-bpf is an eBPF program using kprobes to trace TCP/UDP events (connect, accept, close, send_msg, recv_msg).

The eBPF program is compiled to an ELF object file.

tracer-bpf also provides a Go library that provides a simple API for loading the ELF object file. Internally, it is using the gobpf elf package.

tracer-bpf does not have any run-time dependencies on kernel headers and is not tied to a specific kernel version or kernel configuration. This is quite unusual for eBPF programs using kprobes: for example, eBPF programs using kprobes with bcc are compiled on the fly and depend on kernel headers. And perf tools compiled for one kernel version cannot be used on another kernel version.

To adapt to the currently running kernel at run-time, tracer-bpf creates a series of TCP connections with known parameters (such as known IP addresses and ports) and discovers where those parameters are stored in the kernel struct sock. The offsets of the struct sock fields vary depending on the kernel version and kernel configuration. Since an eBPF programs cannot loop, tracer-bpf does not directly iterate over the possible offsets. It is instead controlled from userspace by the Go library using a state machine.

Development

The easiest way to build and test is inside a Vagrant VM. You can provision the VM by running ./tools/dev_setup.sh and SSHing into the VM with vagrant ssh (vagrant must be installed.)

The VM will mount your local $GOPATH, so you can edit source code with your editor of choice.

make nettop will run a small testing program which periodically prints statistics about TCP/UDP traffic inside the VM.

Documentation

Index

Constants

View Source
const (
	// DEBUGCLIENT is the ClientID for debugging
	DEBUGCLIENT = "-1"
)
View Source
const KprobeProfile = "/sys/kernel/debug/tracing/kprobe_profile"

KprobeProfile is the default path to the kprobe_profile file

Variables

View Source
var (
	// ErrNotImplemented will be returned on non-linux environments like Windows and Mac OSX
	ErrNotImplemented = errors.New("BPF-based system probe not implemented on non-linux systems")
)

Functions

func Asset

func Asset(name string) ([]byte, error)

Asset loads and returns the asset for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetDir

func AssetDir(name string) ([]string, error)

AssetDir returns the file names below a certain directory embedded in the file by go-bindata. For example if you run go-bindata on data/... and data contains the following hierarchy:

data/
  foo.txt
  img/
    a.png
    b.png

then AssetDir("data") would return []string{"foo.txt", "img"} AssetDir("data/img") would return []string{"a.png", "b.png"} AssetDir("foo.txt") and AssetDir("notexist") would return an error AssetDir("") will return []string{"data"}.

func AssetInfo

func AssetInfo(name string) (os.FileInfo, error)

AssetInfo loads and returns the asset info for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetNames

func AssetNames() []string

AssetNames returns the names of the assets.

func BeautifyKey

func BeautifyKey(key string) string

BeautifyKey returns a human readable byte key (used for debugging purposes) it should be in sync with ByteKey Note: This is only used in /debug/* endpoints

func ConnectionSummary

func ConnectionSummary(c ConnectionStats, names map[util.Address][]string) string

ConnectionSummary returns a string summarizing a connection

func CurrentKernelVersion

func CurrentKernelVersion() (uint32, error)

CurrentKernelVersion is not implemented on non-linux systems

func GetProbeStats

func GetProbeStats() map[string]int64

GetProbeStats gathers stats about the # of kprobes triggered /missed by reading the kprobe_profile file

func IsBlacklistedConnection

func IsBlacklistedConnection(scf []*ConnectionFilter, dcf []*ConnectionFilter, conn *ConnectionStats) bool

IsBlacklistedConnection returns true if a given connection should be excluded by the tracer based on user defined filters

func IsTracerSupportedByOS

func IsTracerSupportedByOS(exclusionList []string) (bool, string)

IsTracerSupportedByOS returns whether or not the current kernel version supports tracer functionality along with some context on why it's not supported

func MustAsset

func MustAsset(name string) []byte

MustAsset is like Asset but panics when Asset would return an error. It simplifies safe initialization of global variables.

func RestoreAsset

func RestoreAsset(dir, name string) error

RestoreAsset restores an asset under the given directory

func RestoreAssets

func RestoreAssets(dir, name string) error

RestoreAssets restores an asset under the given directory recursively

Types

type Config

type Config struct {
	// CollectTCPConns specifies whether the tracer should collect traffic statistics for TCP connections
	CollectTCPConns bool

	// CollectUDPConns specifies whether the tracer should collect traffic statistics for UDP connections
	CollectUDPConns bool

	// CollectIPv6Conns specifics whether the tracer should capture traffic for IPv6 TCP/UDP connections
	CollectIPv6Conns bool

	// CollectLocalDNS specifies whether the tracer should capture traffic for local DNS calls
	CollectLocalDNS bool

	// DNSInspection specifies whether the tracer should enhance connection data with domain names by inspecting DNS traffic
	// Notice this does *not* depend on CollectLocalDNS
	DNSInspection bool

	// UDPConnTimeout determines the length of traffic inactivity between two (IP, port)-pairs before declaring a UDP
	// connection as inactive.
	// Note: As UDP traffic is technically "connection-less", for tracking, we consider a UDP connection to be traffic
	//       between a source and destination IP and port.
	UDPConnTimeout time.Duration

	// TCPConnTimeout is like UDPConnTimeout, but for TCP connections. TCP connections are cleared when
	// the BPF module receives a tcp_close call, but TCP connections also age out to catch cases where
	// tcp_close is not intercepted for some reason.
	TCPConnTimeout time.Duration

	// MaxTrackedConnections specifies the maximum number of connections we can track. This determines the size of the eBPF Maps
	MaxTrackedConnections uint

	// MaxClosedConnectionsBuffered represents the maximum number of closed connections we'll buffer in memory. These closed connections
	// get flushed on every client request (default 30s check interval)
	MaxClosedConnectionsBuffered int

	// MaxConnectionsStateBuffered represents the maximum number of state objects that we'll store in memory. These state objects store
	// the stats for a connection so we can accurately determine traffic change between client requests.
	MaxConnectionsStateBuffered int

	// ClientStateExpiry specifies the max time a client (e.g. process-agent)'s state will be stored in memory before being evicted.
	ClientStateExpiry time.Duration

	// ProcRoot is the root path to the proc filesystem
	ProcRoot string

	// BPFDebug enables bpf debug logs
	BPFDebug bool

	// EnableConntrack enables probing conntrack for network address translation via netlink
	EnableConntrack bool

	// ConntrackMaxStateSize specifies the maximum number of connections with NAT we can track
	ConntrackMaxStateSize int

	// ConntrackShortTermBufferSize is the maximum number of short term conntracked connections that will
	// held in memory at once
	ConntrackShortTermBufferSize int

	// DebugPort specifies a port to run golang's expvar and pprof debug endpoint
	DebugPort int

	// ClosedChannelSize specifies the size for closed channel for the tracer
	ClosedChannelSize int

	// ExcludedSourceConnections is a map of source connections to blacklist
	ExcludedSourceConnections map[string][]string

	// ExcludedDestinationConnections is a map of destination connections to blacklist
	ExcludedDestinationConnections map[string][]string
}

Config stores all flags used by the eBPF tracer

func NewDefaultConfig

func NewDefaultConfig() *Config

NewDefaultConfig enables traffic collection for all connection types

func (*Config) EnabledKProbes

func (c *Config) EnabledKProbes(pre410Kernel bool) map[KProbeName]struct{}

EnabledKProbes returns a map of kprobes that are enabled per config settings. This map does not include the probes used exclusively in the offset guessing process.

type ConnTypeFilter

type ConnTypeFilter struct {
	TCP bool
	UDP bool
}

ConnTypeFilter holds user-defined protocols

type ConnectionDirection

type ConnectionDirection uint8

ConnectionDirection indicates if the connection is incoming to the host or outbound

const (
	// INCOMING represents connections inbound to the host
	INCOMING ConnectionDirection = 1

	// OUTGOING represents outbound connections from the host
	OUTGOING ConnectionDirection = 2

	// LOCAL represents connections that don't leave the host
	LOCAL ConnectionDirection = 3

	// NONE represents connections that have no direction (udp, for example)
	NONE ConnectionDirection = 4
)

func (ConnectionDirection) String

func (d ConnectionDirection) String() string

type ConnectionFamily

type ConnectionFamily uint8

ConnectionFamily will be either v4 or v6

const (
	// AFINET represents v4 connections
	AFINET ConnectionFamily = 0

	// AFINET6 represents v6 connections
	AFINET6 ConnectionFamily = 1
)

type ConnectionFilter

type ConnectionFilter struct {
	IP       *net.IPNet // If nil, then all IPs will be considered matching.
	AllPorts ConnTypeFilter

	Ports map[uint16]ConnTypeFilter
}

ConnectionFilter holds a user-defined blacklisted IP/CIDR, and ports

func ParseConnectionFilters

func ParseConnectionFilters(filters map[string][]string) (blacklist []*ConnectionFilter)

ParseConnectionFilters takes the user defined blacklist and returns a slice of ConnectionFilters

type ConnectionStats

type ConnectionStats struct {
	Source util.Address
	Dest   util.Address

	MonotonicSentBytes uint64
	LastSentBytes      uint64

	MonotonicRecvBytes uint64
	LastRecvBytes      uint64

	// Last time the stats for this connection were updated
	LastUpdateEpoch uint64

	MonotonicRetransmits uint32
	LastRetransmits      uint32

	RTT    uint32 // Stored in µs
	RTTVar uint32

	Pid   uint32
	NetNS uint32

	SPort         uint16
	DPort         uint16
	Type          ConnectionType
	Family        ConnectionFamily
	Direction     ConnectionDirection
	IPTranslation *netlink.IPTranslation
	IntraHost     bool
}

ConnectionStats stores statistics for a single connection. Field order in the struct should be 8-byte aligned

func (ConnectionStats) ByteKey

func (c ConnectionStats) ByteKey(buffer *bytes.Buffer) ([]byte, error)

ByteKey returns a unique key for this connection represented as a byte array It's as following:

32b     16b     16b      4b      4b     32/128b      32/128b

| PID | SPORT | DPORT | Family | Type | SrcAddr | DestAddr

func (ConnectionStats) String

func (c ConnectionStats) String() string

type ConnectionType

type ConnectionType uint8

ConnectionType will be either TCP or UDP

const (
	// TCP connection type
	TCP ConnectionType = 0

	// UDP connection type
	UDP ConnectionType = 1
)

func (ConnectionType) String

func (c ConnectionType) String() string

type Connections

type Connections struct {
	DNS   map[util.Address][]string
	Conns []ConnectionStats
}

Connections wraps a collection of ConnectionStats

type KProbeName

type KProbeName string

KProbeName stores the name of the kernel probes setup for tracing

const (
	// TCPv4DestroySock traces the tcp_v4_destroy_sock system call (called for both ipv4 and ipv6)
	TCPv4DestroySock KProbeName = "kprobe/tcp_v4_destroy_sock"

	// TCPv6Connect traces the v6 connect() system call
	TCPv6Connect KProbeName = "kprobe/tcp_v6_connect"
	// TCPv6ConnectReturn traces the return value for the v6 connect() system call
	TCPv6ConnectReturn KProbeName = "kretprobe/tcp_v6_connect"

	// TCPSendMsg traces the tcp_sendmsg() system call
	TCPSendMsg KProbeName = "kprobe/tcp_sendmsg"

	// TCPSendMsgPre410 traces the tcp_sendmsg() system call on kernels prior to 4.1.0. This is created because
	// we need to load a different kprobe implementation
	TCPSendMsgPre410 KProbeName = "kprobe/tcp_sendmsg/pre_4_1_0"

	// TCPSendMsgReturn traces the return value for the tcp_sendmsg() system call
	// XXX: This is only used for telemetry for now to count the number of errors returned
	// by the tcp_sendmsg func (so we can have a # of tcp sent bytes we miscounted)
	TCPSendMsgReturn KProbeName = "kretprobe/tcp_sendmsg"

	// TCPGetInfo traces the tcp_get_info() system call
	// This probe is used for offset guessing only
	TCPGetInfo KProbeName = "kprobe/tcp_get_info"

	// TCPCleanupRBuf traces the tcp_cleanup_rbuf() system call
	TCPCleanupRBuf KProbeName = "kprobe/tcp_cleanup_rbuf"
	// TCPClose traces the tcp_close() system call
	TCPClose KProbeName = "kprobe/tcp_close"

	// UDPSendMsg traces the udp_sendmsg() system call
	UDPSendMsg KProbeName = "kprobe/udp_sendmsg"
	// UDPSendMsgPre410 traces the udp_sendmsg() system call on kernels prior to 4.1.0
	UDPSendMsgPre410 KProbeName = "kprobe/udp_sendmsg/pre_4_1_0"
	// UDPRecvMsg traces the udp_recvmsg() system call
	UDPRecvMsg KProbeName = "kprobe/udp_recvmsg"
	// UDPRecvMsgPre410 traces the udp_recvmsg() system call on kernels prior to 4.1.0
	UDPRecvMsgPre410 KProbeName = "kprobe/udp_recvmsg/pre_4_1_0"
	// UDPRecvMsgReturn traces the return value for the udp_recvmsg() system call
	UDPRecvMsgReturn KProbeName = "kretprobe/udp_recvmsg"

	// TCPRetransmit traces the return value for the tcp_retransmit_skb() system call
	TCPRetransmit KProbeName = "kprobe/tcp_retransmit_skb"

	// InetCskAcceptReturn traces the return value for the inet_csk_accept syscall
	InetCskAcceptReturn KProbeName = "kretprobe/inet_csk_accept"
)

type NetworkState

type NetworkState interface {
	// Connections returns the list of connections for the given client when provided the latest set of active connections
	Connections(clientID string, latestTime uint64, latestConns []ConnectionStats) []ConnectionStats

	// StoreClosedConnection stores a new closed connection
	StoreClosedConnection(conn ConnectionStats)

	// RemoveClient stops tracking stateful data for a given client
	RemoveClient(clientID string)

	// RemoveExpiredClients removes expired clients from the state
	RemoveExpiredClients(now time.Time)

	// RemoveConnections removes the given keys from the state
	RemoveConnections(keys []string)

	// GetStats returns a map of statistics about the current network state
	GetStats() map[string]interface{}

	// DebugNetworkState returns a map with the current network state for a client ID
	DumpState(clientID string) map[string]interface{}
}

NetworkState takes care of handling the logic for: - closed connections - sent and received bytes per connection

func NewDefaultNetworkState

func NewDefaultNetworkState() NetworkState

NewDefaultNetworkState creates a new network state with default settings

func NewNetworkState

func NewNetworkState(clientExpiry time.Duration, maxClosedConns, maxClientStats int) NetworkState

NewNetworkState creates a new network state

type PortMapping

type PortMapping struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

PortMapping tracks which ports a pid is listening on

func NewPortMapping

func NewPortMapping(procRoot string, config *Config) *PortMapping

NewPortMapping creates a new PortMapping instance

func (*PortMapping) AddMapping

func (pm *PortMapping) AddMapping(port uint16)

AddMapping indicates that something is listening on the provided port

func (*PortMapping) IsListening

func (pm *PortMapping) IsListening(port uint16) bool

IsListening returns true if something is listening on the given port

func (*PortMapping) ReadInitialState

func (pm *PortMapping) ReadInitialState() error

ReadInitialState reads the /proc filesystem and determines which ports are being listened on

func (*PortMapping) RemoveMapping

func (pm *PortMapping) RemoveMapping(port uint16)

RemoveMapping indicates that the provided port is no longer being listened on

type ReverseDNS

type ReverseDNS interface {
	Resolve([]ConnectionStats) map[util.Address][]string
	GetStats() map[string]int64
	Close()
}

ReverseDNS translates IPs to names

type Tracer

type Tracer struct{}

Tracer is not implemented on non-linux systems

func NewTracer

func NewTracer(_ *Config) (*Tracer, error)

NewTracer is not implemented on non-linux systems

func (*Tracer) DebugNetworkMaps

func (t *Tracer) DebugNetworkMaps() (*Connections, error)

DebugNetworkMaps is not implemented on non-linux systems

func (*Tracer) DebugNetworkState

func (t *Tracer) DebugNetworkState(clientID string) (map[string]interface{}, error)

DebugNetworkState is not implemented on non-linux systems

func (*Tracer) GetActiveConnections

func (t *Tracer) GetActiveConnections(_ string) (*Connections, error)

GetActiveConnections is not implemented on non-linux systems

func (*Tracer) GetStats

func (t *Tracer) GetStats() (map[string]interface{}, error)

GetStats is not implemented on non-linux systems

func (*Tracer) Stop

func (t *Tracer) Stop()

Stop is not implemented on non-linux systems

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL