Documentation ¶
Overview ¶
Package goebpf provides simple and convenient interface to Linux eBPF system.
Overview ¶
Extended Berkeley Packet Filter (eBPF) is a highly flexible and efficient virtual machine in the Linux kernel allowing to execute bytecode at various hook points in a safe manner. It is actually close to kernel modules which can provide the same functionality, but without cost of kernel panic if something went wrong.
The library is intended to simplify work with eBPF programs. It takes care of low level routine implementation to make it easy to load/run/manage eBPF programs. Currently supported functionality: - Read / parse clang/llmv compiled binaries for eBPF programs / maps - Creates / loads eBPF program / eBPF maps into kernel - Provides simple interface to interact with eBPF maps - Has mock versions of eBPF objects (program, map, etc) in order to make writing unittests simple.
XDP ¶
eXpress Data Path - provides a bare metal, high performance, programmable packet processing at the closest at possible point to network driver. That makes it ideal for speed without compromising programmability. Key benefits includes following:
- It does not require any specialized hardware (program works in kernel’s "VM") - It does not require kernel bypass - It does not replace the TCP/IP stack
Considering very simple and highly effective way to DROP all packets from given source IPv4 address:
XDP program (written in C):
// Simple map to count dropped packets BPF_MAP_DEF(drops) = { .map_type = BPF_MAP_TYPE_PERCPU_ARRAY, .key_size = 4, .value_size = 8, .max_entries = 1, }; BPF_MAP_ADD(drops); SEC("xdp") int xdp_drop(struct xdp_md *ctx) { if (found) { // If some condition (e.g. SRC IP) matches... __u32 idx = 0; // Increase stat by 1 __u64 *stat = bpf_map_lookup_elem(&drops, &idx); if (stat) { *stat += 1; } return XDP_DROP; } return XDP_PASS; }
Once compiled can be used by goebpf in the following way:
bpf := goebpf.NewDefaultEbpfSystem() err := bpf.LoadElf("xdp.elf") program := bpf.GetProgramByName("xdp_drop") // name matches function name in C err = program.Load() // Load program into kernel err = program.Attach("eth0") // Attach to interface defer program.Detach() // Interact with program is simply done through maps: drops := bpf.GetMapByName("drops") // name also matches BPF_MAP_ADD(drops) val, err := drops.LookupInt(0) // Get value from map at index 0 if err == nil { fmt.Printf("Drops: %d\n", val) }
PerfEvents ¶
Perf Events (originally Performance Counters for Linux) is powerful kernel instrument for tracing, profiling and a lot of other cases like general events to user space.
Usually it is implemented using special eBPF map type "BPF_MAP_TYPE_PERF_EVENT_ARRAY" as a container to send events into.
A simple example could be to log all TCP SYN packets into user space from XDP program:
BPF_MAP_DEF(perfmap) = { .map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .max_entries = 128, // Up to 128 CPUs }; BPF_MAP_ADD(perfmap); SEC("xdp") int xdp_dump(struct xdp_md *ctx) { // ... if (tcp->syn) { // Log event to user space bpf_perf_event_output(ctx, &perfmap, BPF_F_CURRENT_CPU, &evt, sizeof(evt)); } } bpf := goebpf.NewDefaultEbpfSystem() bpf.LoadElf("xdp.elf") program := bpf.GetProgramByName("xdp_dump") // name matches function name in C err = program.Load() // Load program into kernel err = program.Attach("eth0") // Attach to interface defer program.Detach() // Start listening to Perf Events perf, err := goebpf.NewPerfEvents(perfmap) // 4096 is ring buffer size perfEvents, err := perf.StartForAllProcessesAndCPUs(4096) defer perf.Stop() for { select { case eventData := <-perfEvents: fmt.Println(eventData) } }
Kprobes ¶
There are currently two types of supported probes: kprobes, and kretprobes (also called return probes). A kprobe can be inserted on virtually any instruction in the kernel. A return probe fires when a specified function returns.
For example, you can trigger eBPF code to run when a kernel function starts by attaching the program to a “kprobe” event. Because it runs in the kernel, eBPF code is extremely high performance.
A simple example could be to log all process execution events into user space from Kprobe program:
BPF_MAP_DEF(events) = { .map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .max_entries = 1024, }; BPF_MAP_ADD(events); SEC("kprobe/guess_execve") int execve_entry(struct pt_regs *ctx) { // ... event_t e = {0}; e.ktime_ns = bpf_ktime_get_ns(); e.pid = bpf_get_current_pid_tgid() >> 32; e.uid = bpf_get_current_uid_gid() >> 32; e.gid = bpf_get_current_uid_gid(); bpf_get_current_comm(&e.comm, sizeof(e.comm)); buf_write(buf, (void *)&e, sizeof(e)); buf_strcat(buf, (void *)args[0]); buf_strcat_argv(buf, (void *)args[1]); buf_perf_output(ctx); return 0; } // Cleanup old probes err := goebpf.CleanupProbes() // Load eBPF compiled binary bpf := goebpf.NewDefaultEbpfSystem() bpf.LoadElf("kprobe.elf") program := bpf.GetProgramByName("kprobe") // name matches function name in C // Attach kprobes err = p.AttachProbes() // Detach them once done defer p.DetachProbes()
Index ¶
- Constants
- func CleanupProbes() error
- func CreateLPMtrieKey(s string) *net.IPNet
- func GetNumOfPossibleCpus() (int, error)
- func KeyValueToBytes(ival interface{}, size int) ([]byte, error)
- func KtimeToTime(ktime uint64) time.Time
- func ListProbes() ([]string, error)
- func NewMmapRingBuffer(ptr unsafe.Pointer) *mmapRingBuffer
- func NullTerminatedStringToString(val []byte) string
- func ParseFlexibleIntegerLittleEndian(rawVal []byte) uint64
- type BaseProgram
- func (prog *BaseProgram) Close() error
- func (prog *BaseProgram) GetFd() int
- func (prog *BaseProgram) GetLicense() string
- func (prog *BaseProgram) GetName() string
- func (prog *BaseProgram) GetSection() string
- func (prog *BaseProgram) GetSize() int
- func (prog *BaseProgram) GetType() ProgramType
- func (prog *BaseProgram) Load() error
- func (prog *BaseProgram) Pin(path string) error
- type EbpfMap
- func (m *EbpfMap) CloneTemplate() Map
- func (m *EbpfMap) Close() error
- func (m *EbpfMap) Create() error
- func (m *EbpfMap) Delete(ikey interface{}) error
- func (m *EbpfMap) GetFd() int
- func (m *EbpfMap) GetName() string
- func (m *EbpfMap) GetNextKey(ikey interface{}) ([]byte, error)
- func (m *EbpfMap) GetNextKeyInt(ikey interface{}) (int, error)
- func (m *EbpfMap) GetNextKeyString(ikey interface{}) (string, error)
- func (m *EbpfMap) GetNextKeyUint64(ikey interface{}) (uint64, error)
- func (m *EbpfMap) GetType() MapType
- func (m *EbpfMap) Insert(ikey interface{}, ivalue interface{}) error
- func (m *EbpfMap) Lookup(ikey interface{}) ([]byte, error)
- func (m *EbpfMap) LookupInt(ikey interface{}) (int, error)
- func (m *EbpfMap) LookupString(ikey interface{}) (string, error)
- func (m *EbpfMap) LookupUint64(ikey interface{}) (uint64, error)
- func (m *EbpfMap) Update(ikey interface{}, ivalue interface{}) error
- func (m *EbpfMap) Upsert(ikey interface{}, ivalue interface{}) error
- type KprobeAttachType
- type Map
- type MapType
- type PerfEvents
- type Program
- type ProgramInfo
- type ProgramType
- type SocketFilterAttachParams
- type SocketFilterAttachType
- type SocketFilterResult
- type System
- type XdpAttachMode
- type XdpAttachParams
- type XdpResult
Constants ¶
const ( // MapSectionName is name of ELF section for maps MapSectionName = "maps" // LicenseSectionName is name of ELF section for license info LicenseSectionName = "license" )
const ( SocketFilterDeny SocketFilterResult = C.SOCKET_FILTER_DENY SocketFilterAllow SocketFilterResult = C.SOCKET_FILTER_ALLOW SocketAttachTypeFilter SocketFilterAttachType = SO_ATTACH_BPF SocketAttachTypeReusePort SocketFilterAttachType = SO_ATTACH_REUSEPORT_EBPF // Constants from Linux kernel, they dont' present in "golang.org/x/sys/unix" SO_ATTACH_BPF = 50 SO_ATTACH_REUSEPORT_EBPF = 52 SO_DETACH_FILTER = 27 )
Variables ¶
This section is empty.
Functions ¶
func CleanupProbes ¶
func CleanupProbes() error
CleanupProbes attempts to detach all kprobe entries containing our namespace string.
func CreateLPMtrieKey ¶
CreateLPMtrieKey converts string representation of CIDR into net.IPNet in order to support special eBPF map type: LPMtrie ("Longest Prefix Match Trie") Can be used to match single IPv4/6 address with multiple CIDRs, like
m.Insert(CreateLPMtrieKey("192.168.0.0/16"), "value16") m.Insert(CreateLPMtrieKey("192.168.0.0/24"), "value24") value, err := m.LookupString(CreateLPMtrieKey("192.168.0.10"))
IP 192.168.0.10 matches both CIDRs, however lookup value will be "value24" since /24 prefix is smaller than /16
func GetNumOfPossibleCpus ¶
GetNumOfPossibleCpus returns number of CPU available to eBPF program NOTE: this is not the same as runtime.NumCPU()
func KeyValueToBytes ¶
KeyValueToBytes coverts interface representation of key/value into bytes
func KtimeToTime ¶
KtimeToTime converts kernel time (nanoseconds since boot) to time.Time
func ListProbes ¶
ListProbes returns a list of the systems attached kprobes as returned by debugfs.
func NewMmapRingBuffer ¶
NewMmapRingBuffer creates mmapRingBuffer instance from pre-created mmap memory pointer ptr
func NullTerminatedStringToString ¶
NullTerminatedStringToString is helper to convert null terminated string to GO string
func ParseFlexibleIntegerLittleEndian ¶
ParseFlexibleIntegerLittleEndian converts flexible amount of bytes into little endian integer, e.g.: {1} -> 1 {0xe8, 0x3} -> 10000
Types ¶
type BaseProgram ¶
type BaseProgram struct {
// contains filtered or unexported fields
}
BaseProgram is common shared fields of eBPF programs
func (*BaseProgram) Close ¶
func (prog *BaseProgram) Close() error
Close unloads program from kernel
func (*BaseProgram) GetFd ¶
func (prog *BaseProgram) GetFd() int
GetFd returns program's file description
func (*BaseProgram) GetLicense ¶
func (prog *BaseProgram) GetLicense() string
GetLicense returns program's license
func (*BaseProgram) GetName ¶
func (prog *BaseProgram) GetName() string
GetName returns program name as defined in C code
func (*BaseProgram) GetSection ¶
func (prog *BaseProgram) GetSection() string
GetSection returns section name for the program
func (*BaseProgram) GetSize ¶
func (prog *BaseProgram) GetSize() int
GetSize returns eBPF bytecode size in bytes
func (*BaseProgram) GetType ¶
func (prog *BaseProgram) GetType() ProgramType
GetType returns program type
func (*BaseProgram) Load ¶
func (prog *BaseProgram) Load() error
Load loads program into linux kernel
func (*BaseProgram) Pin ¶
func (prog *BaseProgram) Pin(path string) error
Pin saves ("pins") file description into special file on filesystem so it can be accessed from other processes. WARNING: destination filesystem must be mounted as "bpf" (mount -t bpf)
type EbpfMap ¶
type EbpfMap struct { // Map name, picked up automatically by loader from ELF section Name string Type MapType KeySize int ValueSize int MaxEntries int Flags int // Name of eBPF map used as template for all inner maps. Only for array/hash of maps InnerMapName string InnerMapFd int // Persistent eBPF map use case: contains path to special file in filesystem. // WARNING: filesystem must be mounted as BPF PersistentPath string // contains filtered or unexported fields }
EbpfMap is structure to define eBPF map
func NewMapFromExistingMapByFd ¶
NewMapFromExistingMapByFd creates eBPF map from already existing map by fd available to current process (i.e. created by it). In other words it will work only on maps created by current process.
func NewMapFromExistingMapById ¶
NewMapFromExistingMapById creates eBPF map from BPF object ID. BPF object ID is a kernel mechanism to let non owner process to use BPF objects. Common use case - tooling for troubleshoot / inspect existing BPF objects in the kernel.
func NewMapFromExistingMapByPath ¶
NewMapFromExistingMapMapByPath creates eBPF map from a pinned BPF object path. Pinned BPF object is a kernel mechanism to let non owner process to use BPF objects. Common use case - tooling for troubleshoot / inspect existing BPF objects in the kernel.
func (*EbpfMap) CloneTemplate ¶
CloneTemplate creates new instance of eBPF map using current map parameters. Main use case is work with array/hash of maps:
// Create new map based on template newItem := templateMap.CloneTemplate() newItem.Create() // Insert item into array of maps superMap.Insert(1, newItem)
func (*EbpfMap) GetNextKey ¶
GetNextKey looks up next key in the map. returns 'next_key' on success, 'err' on failure (or last key in map - no next key available).
func (*EbpfMap) GetNextKeyInt ¶
GetNextKeyInt looks up next key in the map and returns it as int.
func (*EbpfMap) GetNextKeyString ¶
GetNextKeyString looks up next key in the map and returns it as a golang string.
func (*EbpfMap) GetNextKeyUint64 ¶
GetNextKeyUint64 looks up next key in the map and returns it as uint64.
func (*EbpfMap) Insert ¶
Insert inserts value into eBPF map at given ikey. Supported key/value types are: int, uint8, uint16, uint32, int32, uint64, string, []byte, net.IPNet
func (*EbpfMap) Lookup ¶
Lookup performs lookup and returns array of bytes WARNING: For Per-CPU array/hash map return value will contain data from all CPUs, i.e. length = roundUp(valueSize, 8) * nCPU
func (*EbpfMap) LookupInt ¶
LookupInt performs lookup and returns integer WARNING: For Per-CPU array/hash returns sum of values from all CPUs
func (*EbpfMap) LookupString ¶
LookupString perform lookup and returns GO string from NULL terminated C string WARNING: Does NOT work for Per-CPU maps (not an real use case?).
func (*EbpfMap) LookupUint64 ¶
LookupUint64 performs lookup and returns uint64 WARNING: For Per-CPU array/hash returns sum of values from all CPUs
type KprobeAttachType ¶
type KprobeAttachType int
KprobeAttachType specified whether a Kprobe program is attached as an entry or exit probe.
const ( KprobeAttachTypeEntry KprobeAttachType = iota KprobeAttachTypeReturn )
func (KprobeAttachType) Prefix ¶
func (t KprobeAttachType) Prefix() string
Prefix returns the string prefix used by debugfs actions.
func (KprobeAttachType) String ¶
func (t KprobeAttachType) String() string
String returns a human readable string for a kprobe attach type.
type Map ¶
type Map interface { Create() error GetFd() int GetName() string GetType() MapType Close() error // Makes a copy of map definition. This will NOT create map, just copies definition, "template". // Useful for array/map of maps use case CloneTemplate() Map // Generic lookup. Accepts any type which will be // converted to []byte eventually, returns bytes Lookup(interface{}) ([]byte, error) // The same, but does casting of return value to int / uint64 LookupInt(interface{}) (int, error) LookupUint64(interface{}) (uint64, error) // The same, but does casting of return value to string LookupString(interface{}) (string, error) Insert(interface{}, interface{}) error Update(interface{}, interface{}) error Upsert(interface{}, interface{}) error Delete(interface{}) error // Implementation of bpf_map_get_next_key GetNextKey(interface{}) ([]byte, error) GetNextKeyString(interface{}) (string, error) GetNextKeyInt(interface{}) (int, error) GetNextKeyUint64(interface{}) (uint64, error) }
Map defines interface to interact with eBPF maps
type MapType ¶
type MapType int
MapType is eBPF map type enum
const ( MapTypeHash MapType = C.BPF_MAP_TYPE_HASH MapTypeArray MapType = C.BPF_MAP_TYPE_ARRAY MapTypeProgArray MapType = C.BPF_MAP_TYPE_PROG_ARRAY MapTypePerfEventArray MapType = C.BPF_MAP_TYPE_PERF_EVENT_ARRAY MapTypePerCPUHash MapType = C.BPF_MAP_TYPE_PERCPU_HASH MapTypePerCPUArray MapType = C.BPF_MAP_TYPE_PERCPU_ARRAY MapTypeStackTrace MapType = C.BPF_MAP_TYPE_STACK_TRACE MapTypeCgroupArray MapType = C.BPF_MAP_TYPE_CGROUP_ARRAY MapTypeLRUHash MapType = C.BPF_MAP_TYPE_LRU_HASH MapTypeLRUPerCPUHash MapType = C.BPF_MAP_TYPE_LRU_PERCPU_HASH MapTypeLPMTrie MapType = C.BPF_MAP_TYPE_LPM_TRIE MapTypeArrayOfMaps MapType = C.BPF_MAP_TYPE_ARRAY_OF_MAPS MapTypeHashOfMaps MapType = C.BPF_MAP_TYPE_HASH_OF_MAPS MapTypeDevMap MapType = C.BPF_MAP_TYPE_DEVMAP MapTypeSockMap MapType = C.BPF_MAP_TYPE_SOCKMAP MapTypeCPUMap MapType = C.BPF_MAP_TYPE_CPUMAP MapTypeXSKMap MapType = C.BPF_MAP_TYPE_XSKMAP MapTypeSockHash MapType = C.BPF_MAP_TYPE_SOCKHASH MapTypeCGroupStorage MapType = C.BPF_MAP_TYPE_CGROUP_STORAGE MapTypeReusePortSockArray MapType = C.BPF_MAP_TYPE_REUSEPORT_SOCKARRAY MapTypePerCpuCGroupStorage MapType = C.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE MapTypeQueue MapType = C.BPF_MAP_TYPE_QUEUE MapTypeStack MapType = C.BPF_MAP_TYPE_STACK MapTypeSKStorage MapType = C.BPF_MAP_TYPE_SK_STORAGE )
Supported eBPF map types.
type PerfEvents ¶
type PerfEvents struct { // Statistics EventsReceived int EventsLost int EventsUnknowType int // PollTimeoutMs is timeout for blocking call of poll() // Defaults to 100ms PollTimeoutMs int // contains filtered or unexported fields }
PerfEvents is a way to interact with Linux's PerfEvents for eBPF cases.
func NewPerfEvents ¶
func NewPerfEvents(m Map) (*PerfEvents, error)
NewPerfEvents creates new instance of PerfEvents for eBPF map "m". "m" must be a type of "MapTypePerfEventArray"
func (*PerfEvents) StartForAllProcessesAndCPUs ¶
func (pe *PerfEvents) StartForAllProcessesAndCPUs(bufferSize int) (<-chan []byte, error)
StartForAllProcessesAndCPUs starts PerfEvent polling on all CPUs for all system processes This mode requires specially organized map: index matches CPU ID. "bufferSize" is ring buffer size for perfEvents. Per CPU. All updates will be sent into returned channel.
type Program ¶
type Program interface { // Load program into Linux kernel Load() error // Pin (save, share) program into given location. // Location must be mounted as bpffs (mount bpffs -t bpffs /some/location) Pin(path string) error // Unload program from kernel Close() error // Attach program to something - depends on program type. // - XDP: Attach to network interface (data - iface name, or XdpAttachParams) // - SocketFilter: Attach to socket (data - socket fd) Attach(data interface{}) error // Detach previously attached program Detach() error // Returns program name as it defined in C code GetName() string // Returns section name for the program GetSection() string // Returns program file descriptor (given by kernel) GetFd() int // Returns size of program in bytes GetSize() int // Returns program's license GetLicense() string // Returns program type GetType() ProgramType }
Program defines eBPF program interface
type ProgramInfo ¶
type ProgramInfo struct { Name string Tag string // Program tag (unclear what this for) Type ProgramType Id int // ID - external ID of program (to refer object) Fd int // fd - local process fd to be able to access the object. JitedProgramLen int // Size of program in CPU instructions XlatedProgramLen int // Size of program in bytecode LoadTime time.Time CreatedByUid int // UID of creator Maps map[string]Map // Associated eBPF maps }
ProgramInfo - information of already loaded eBPF program info
Main use case is to inspect already loaded into kernel programs.
func GetProgramInfoByFd ¶
func GetProgramInfoByFd(fd int) (*ProgramInfo, error)
GetProgramInfoByFd queries information about already loaded eBPF program by fd (fd belongs to local process, cannot be shared)
func GetProgramInfoById ¶
func GetProgramInfoById(id int) (*ProgramInfo, error)
GetProgramInfoById queries information about already loaded eBPF program by external ID.
type ProgramType ¶
type ProgramType int
ProgramType is eBPF program types enum
const ( ProgramTypeUnspec ProgramType = iota ProgramTypeSocketFilter ProgramTypeKprobe ProgramTypeSchedCls ProgramTypeSchedAct ProgramTypeTracepoint ProgramTypeXdp ProgramTypePerfEvent ProgramTypeCgroupSkb ProgramTypeCgroupSock ProgramTypeLwtIn ProgramTypeLwtOut ProgramTypeLwtXmit ProgramTypeSockOps )
Must be in sync with enum bpf_prog_type from <linux/bpf.h>
func (ProgramType) String ¶
func (t ProgramType) String() string
type SocketFilterAttachParams ¶
type SocketFilterAttachParams struct { // SocketFd is socket file descriptor returned by unix.Socket(...) SocketFd int // AttachType is one of SocketAttachTypeFilter / SocketAttachTypeReusePort // depending on use case AttachType SocketFilterAttachType }
SocketFilterAttachParams is accepted as argument to Program.Attach()
type SocketFilterAttachType ¶
type SocketFilterAttachType int
SocketFilterAttachType is either SO_ATTACH_BPF or SO_ATTACH_REUSEPORT_EBPF
func (SocketFilterAttachType) String ¶
func (t SocketFilterAttachType) String() string
type SocketFilterResult ¶
type SocketFilterResult int
SocketFilterResult is eBPF program return code enum
func (SocketFilterResult) String ¶
func (t SocketFilterResult) String() string
type System ¶
type System interface { // Read previously compiled eBPF program at the given path LoadElf(path string) error // Read previously compiled eBPF program from an io.ReaderAt Load(reader io.ReaderAt) error // Get all defined eBPF maps GetMaps() map[string]Map // Returns Map or nil if not found GetMapByName(name string) Map // Get all eBPF programs GetPrograms() map[string]Program // Returns Program or nil if not found GetProgramByName(name string) Program }
System defines interface for eBPF system - top level interface to interact with eBPF system
func NewDefaultEbpfSystem ¶
func NewDefaultEbpfSystem() System
NewDefaultEbpfSystem creates default eBPF system
type XdpAttachMode ¶
type XdpAttachMode int
XdpAttachMode selects a way how XDP program will be attached to interface
const ( // XdpAttachModeNone stands for "best effort" - kernel automatically // selects best mode (would try Drv first, then fallback to Generic). // NOTE: Kernel will not fallback to Generic XDP if NIC driver failed // to install XDP program. XdpAttachModeNone XdpAttachMode = 0 // XdpAttachModeSkb is "generic", kernel mode, less performant comparing to native, // but does not requires driver support. XdpAttachModeSkb XdpAttachMode = (1 << 1) // XdpAttachModeDrv is native, driver mode (support from driver side required) XdpAttachModeDrv XdpAttachMode = (1 << 2) // XdpAttachModeHw suitable for NICs with hardware XDP support XdpAttachModeHw XdpAttachMode = (1 << 3) )
type XdpAttachParams ¶
type XdpAttachParams struct { // Interface is string name of interface to attach program to Interface string // Mode is one of XdpAttachMode. Mode XdpAttachMode }
XdpAttachParams used to pass parameters to Attach() call.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
examples
|
|
wrapper
Package wrapper is wrapper for "cross compiled" XDP in order to call it right from GO P.S. this should be as part of *_test.go files, however, GO does not support using import "C" from tests...
|
Package wrapper is wrapper for "cross compiled" XDP in order to call it right from GO P.S. this should be as part of *_test.go files, however, GO does not support using import "C" from tests... |