Package bpf implements marshaling and unmarshaling of programs for the Berkeley Packet Filter virtual machine, and provides a Go implementation of the virtual machine.
BPF's main use is to specify a packet filter for network taps, so that the kernel doesn't have to expensively copy every packet it sees to userspace. However, it's been repurposed to other areas where running user code in-kernel is needed. For example, Linux's seccomp uses BPF to apply security policies to system calls.
import "golang.org/x/net/bpf"The BPF VM is an accumulator machine. Its main register, called register A, is an implicit source and destination in all arithmetic and logic operations. The machine also has 16 scratch registers for temporary storage, and an indirection register (register X) for indirect memory access. All registers are 32 bits wide.
Each run of a BPF program is given one packet, which is placed in the VM's read-only "main memory". LoadAbsolute and LoadIndirect instructions can fetch up to 32 bits at a time into register A for examination.
The goal of a BPF program is to produce and return a verdict (uint32), which tells the kernel what to do with the packet. In the context of packet filtering, the returned value is the number of bytes of the packet to forward to userspace, or 0 to ignore the packet.
// Assemble converts instructions into raw instructions suitable for loading into a BPF virtual machine
func Assemble(insts []Instruction) ([]RawInstruction, error)
// Disassemble attempts to parse raw back into Instructions
func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool)// VM is an emulated BPF virtual machine
type VM struct {
// Has unexported fields
}
// NewVM returns a new VM using the input BPF program
func NewVM(filter []Instruction) (*VM, error)
// Run runs the VM's BPF program against the input bytes
func (v *VM) Run(in []byte) (int, error)// Instruction is one instruction executed by the BPF virtual machine
type Instruction interface {
// Assemble assembles the Instruction into a RawInstruction
Assemble() (RawInstruction, error)
}
// RawInstruction is a raw BPF virtual machine instruction
type RawInstruction struct {
Op uint16 // Operation to execute
Jt uint8 // Jump if true count
Jf uint8 // Jump if false count
K uint32 // Constant parameter (meaning depends on Op)
}
func (ri RawInstruction) Assemble() (RawInstruction, error)
func (ri RawInstruction) Disassemble() Instruction// LoadAbsolute loads packet[Off:Off+Size] as an integer value into register A
type LoadAbsolute struct {
Off uint32
Size int // 1, 2 or 4
}
func (a LoadAbsolute) Assemble() (RawInstruction, error)
func (a LoadAbsolute) String() string
// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value into register A
type LoadIndirect struct {
Off uint32
Size int // 1, 2 or 4
}
func (a LoadIndirect) Assemble() (RawInstruction, error)
func (a LoadIndirect) String() string
// LoadConstant loads Val into register Dst
type LoadConstant struct {
Dst Register
Val uint32
}
func (a LoadConstant) Assemble() (RawInstruction, error)
func (a LoadConstant) String() string
// LoadScratch loads scratch[N] into register Dst
type LoadScratch struct {
Dst Register
N int // 0-15
}
func (a LoadScratch) Assemble() (RawInstruction, error)
func (a LoadScratch) String() string
// LoadMemShift multiplies the first 4 bits of the byte at packet[Off] by 4 and stores in register X
type LoadMemShift struct {
Off uint32
}
func (a LoadMemShift) Assemble() (RawInstruction, error)
func (a LoadMemShift) String() string
// LoadExtension invokes a linux-specific extension and stores the result in register A
type LoadExtension struct {
Num Extension
}
func (a LoadExtension) Assemble() (RawInstruction, error)
func (a LoadExtension) String() string// StoreScratch stores register Src into scratch[N]
type StoreScratch struct {
Src Register
N int // 0-15
}
func (a StoreScratch) Assemble() (RawInstruction, error)
func (a StoreScratch) String() string// ALUOp is an arithmetic or logic operation
type ALUOp uint16
const (
ALUOpAdd ALUOp = iota << 4
ALUOpSub
ALUOpMul
ALUOpDiv
ALUOpOr
ALUOpAnd
ALUOpShiftLeft
ALUOpShiftRight
ALUOpMod
ALUOpXor
)
// ALUOpConstant executes A = A <Op> Val
type ALUOpConstant struct {
Op ALUOp
Val uint32
}
func (a ALUOpConstant) Assemble() (RawInstruction, error)
func (a ALUOpConstant) String() string
// ALUOpX executes A = A <Op> X
type ALUOpX struct {
Op ALUOp
}
func (a ALUOpX) Assemble() (RawInstruction, error)
func (a ALUOpX) String() string
// NegateA executes A = -A
type NegateA struct{}
func (a NegateA) Assemble() (RawInstruction, error)
func (a NegateA) String() string// JumpTest is a comparison operator used in conditional jumps
type JumpTest uint16
const (
JumpEqual JumpTest = iota // K == A
JumpNotEqual // K != A
JumpGreaterThan // K > A
JumpLessThan // K < A
JumpGreaterOrEqual // K >= A
JumpLessOrEqual // K <= A
JumpBitsSet // K & A != 0
JumpBitsNotSet // K & A == 0
)
// Jump skips the following Skip instructions in the program
type Jump struct {
Skip uint32
}
func (a Jump) Assemble() (RawInstruction, error)
func (a Jump) String() string
// JumpIf skips the following Skip instructions if A <Cond> Val is true
type JumpIf struct {
Cond JumpTest
Val uint32
SkipTrue uint8
SkipFalse uint8
}
func (a JumpIf) Assemble() (RawInstruction, error)
func (a JumpIf) String() string
// JumpIfX skips the following Skip instructions if A <Cond> X is true
type JumpIfX struct {
Cond JumpTest
SkipTrue uint8
SkipFalse uint8
}
func (a JumpIfX) Assemble() (RawInstruction, error)
func (a JumpIfX) String() string// RetA exits the BPF program, returning the value of register A
type RetA struct{}
func (a RetA) Assemble() (RawInstruction, error)
func (a RetA) String() string
// RetConstant exits the BPF program, returning a constant value
type RetConstant struct {
Val uint32
}
func (a RetConstant) Assemble() (RawInstruction, error)
func (a RetConstant) String() string// Register is a register of the BPF virtual machine
type Register uint16
const (
RegA Register = iota // Accumulator register (destination of ALU operations)
RegX // Indirection register (used by LoadIndirect operations)
)
// TAX copies the value of register A to register X
type TAX struct{}
func (a TAX) Assemble() (RawInstruction, error)
func (a TAX) String() string
// TXA copies the value of register X to register A
type TXA struct{}
func (a TXA) Assemble() (RawInstruction, error)
func (a TXA) String() string// Extension is a kernel function call for advanced operations
type Extension int
const (
ExtLen Extension = 1 // Returns the length of the packet
ExtProto Extension = 0 // Returns the packet's L3 protocol type
ExtType Extension = 4 // Returns the packet's type (skb->pkt_type)
ExtPayloadOffset Extension = 52 // Returns the offset of the packet payload
ExtInterfaceIndex Extension = 8 // Returns the index of the interface
ExtNetlinkAttr Extension = 12 // Returns the netlink attribute of type X at offset A
ExtNetlinkAttrNested Extension = 16 // Returns nested netlink attribute of type X at offset A
ExtMark Extension = 20 // Returns the packet's mark value
ExtQueue Extension = 24 // Returns the packet's assigned hardware queue
ExtLinkLayerType Extension = 28 // Returns the packet's hardware address type
ExtRXHash Extension = 32 // Returns the packet's receive hash
ExtCPUID Extension = 36 // Returns the ID of the CPU processing the packet
ExtVLANTag Extension = 44 // Returns the packet's VLAN tag
ExtVLANTagPresent Extension = 48 // Returns non-zero if packet has a VLAN tag
ExtVLANProto Extension = 60 // Returns VLAN protocol (0x8100, 0x88a8, etc.)
ExtRand Extension = 56 // Returns a uniformly random uint32
)// Setter is a type which can attach a compiled BPF filter to itself
type Setter interface {
SetBPF(filter []RawInstruction) error
}// Select all ARP packets
filter, err := bpf.Assemble([]bpf.Instruction{
// Load "EtherType" field from the ethernet header
bpf.LoadAbsolute{Off: 12, Size: 2},
// Skip over the next instruction if EtherType is not ARP
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1},
// Verdict: send up to 4k of the packet to userspace
bpf.RetConstant{Val: 4096},
// Verdict: ignore packet
bpf.RetConstant{Val: 0},
})
if err != nil {
panic(err)
}// Capture a random 1% sample of traffic
filter, err := bpf.Assemble([]bpf.Instruction{
// Get a 32-bit random number from the Linux kernel
bpf.LoadExtension{Num: bpf.ExtRand},
// 1% dice roll?
bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2 ^ 32 / 100, SkipFalse: 1},
// Capture
bpf.RetConstant{Val: 4096},
// Ignore
bpf.RetConstant{Val: 0},
})
if err != nil {
panic(err)
}// Create a BPF VM with a program
program := []bpf.Instruction{
bpf.LoadAbsolute{Off: 12, Size: 2},
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x0800, SkipTrue: 1},
bpf.RetConstant{Val: 0},
bpf.RetConstant{Val: 4096},
}
vm, err := bpf.NewVM(program)
if err != nil {
panic(err)
}
// Run the VM against packet data
packetData := []byte{/* ... packet bytes ... */}
verdict, err := vm.Run(packetData)
if err != nil {
panic(err)
}
if verdict > 0 {
fmt.Printf("Accept %d bytes\n", verdict)
} else {
fmt.Println("Reject packet")
}