├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .golangci.yml ├── LICENCE.txt ├── README.md ├── bpfsys ├── bpf_attr.go ├── doc.go └── syscall.go ├── bpftypes ├── bpf_types.go └── doc.go ├── btf.go ├── btf_test.go ├── cmd ├── examples │ ├── Example.md │ ├── bpf_to_bpf │ │ ├── main.go │ │ └── src │ │ │ ├── bpf_endian.h │ │ │ ├── compile.sh │ │ │ ├── xdp │ │ │ └── xdp.c │ ├── icmp_pcap │ │ ├── main.go │ │ └── src │ │ │ ├── compile.sh │ │ │ ├── sockfilter │ │ │ └── sockfilter.c │ ├── kprobe_execve_stats │ │ ├── bpf │ │ │ ├── .gitignore │ │ │ ├── compile.sh │ │ │ ├── tracex1_kern │ │ │ └── tracex1_kern.c │ │ └── main.go │ ├── map_batch │ │ └── main.go │ ├── map_benchmark │ │ └── main.go │ ├── map_in_map │ │ ├── bpf │ │ │ ├── compile.sh │ │ │ ├── map_in_map_counter │ │ │ └── map_in_map_counter.c │ │ └── main.go │ ├── map_iterator │ │ └── main.go │ ├── map_pinning │ │ └── main.go │ ├── per_cpu_map │ │ ├── bpf │ │ │ ├── compile.sh │ │ │ ├── percpu │ │ │ └── percpu.c │ │ └── main.go │ ├── tailcall │ │ ├── main.go │ │ └── src │ │ │ ├── bpf_endian.h │ │ │ ├── compile.sh │ │ │ ├── xdp │ │ │ ├── xdp.c │ │ │ └── xdp.o │ ├── test_xdp_program │ │ └── main.go │ ├── udp_socket_filter │ │ ├── main.go │ │ └── src │ │ │ ├── compile.sh │ │ │ ├── sockfilter │ │ │ └── sockfilter.c │ ├── uprobe_bash_stats │ │ ├── bpf │ │ │ ├── .gitignore │ │ │ ├── bash_stats │ │ │ ├── bash_stats.c │ │ │ └── compile.sh │ │ └── main.go │ ├── xdp_stats │ │ ├── bpf │ │ │ ├── basic03_map_counter │ │ │ ├── basic03_map_counter.c │ │ │ └── compile.sh │ │ └── main.go │ ├── xdp_stats_assembly │ │ └── main.go │ ├── xdp_stats_instructions │ │ └── main.go │ ├── xsk_echo_reply │ │ └── main.go │ └── xsk_multi_sock │ │ └── main.go └── testsuite │ ├── .gitignore │ ├── Readme.md │ ├── html.go │ ├── integration │ ├── doc.go │ ├── ebpf │ │ ├── compile.sh │ │ ├── global_data_test │ │ ├── global_data_test.c │ │ ├── xdp_stats_test │ │ └── xdp_stats_test.c │ ├── global_data_test.go │ ├── map_pinning_test.go │ ├── xdp_stats_test.go │ └── xsk_test.go │ └── main.go ├── ebpf ├── add.go ├── and.go ├── arsh.go ├── asm.go ├── asm_test.bpfasm ├── asm_test.go ├── atomic.go ├── byteorder.go ├── call_bpf.go ├── call_helper.go ├── decode.go ├── div.go ├── doc.go ├── ebpf.go ├── end.go ├── exit.go ├── ja.go ├── jeq.go ├── jge.go ├── jgt.go ├── jle.go ├── jlt.go ├── jne.go ├── jset.go ├── jsge.go ├── jsgt.go ├── jsle.go ├── jslt.go ├── load.go ├── lsh.go ├── mod.go ├── mov.go ├── mul.go ├── neg.go ├── or.go ├── rsh.go ├── store.go ├── sub.go └── xor.go ├── elf.go ├── emulator ├── doc.go ├── helper_functions.go ├── inst.go ├── inst_add.go ├── inst_and.go ├── inst_arsh.go ├── inst_atomic.go ├── inst_call_bpf.go ├── inst_call_helper.go ├── inst_div.go ├── inst_end.go ├── inst_exit.go ├── inst_ja.go ├── inst_jeq.go ├── inst_jge.go ├── inst_jgt.go ├── inst_jle.go ├── inst_jlt.go ├── inst_jne.go ├── inst_jset.go ├── inst_jsge.go ├── inst_jsgt.go ├── inst_jsle.go ├── inst_jslt.go ├── inst_load.go ├── inst_lsh.go ├── inst_mod.go ├── inst_mov.go ├── inst_mul.go ├── inst_neg.go ├── inst_nop.go ├── inst_or.go ├── inst_rsh.go ├── inst_store.go ├── inst_sub.go ├── inst_xor.go ├── linux_errno.go ├── maps.go ├── maps_array.go ├── maps_hash.go ├── maps_hash_lru.go ├── maps_hash_lru_test.go ├── maps_perf_event_array.go ├── maps_queue.go ├── maps_stack.go ├── memory.go ├── registers.go ├── todo.md └── vm.go ├── go.mod ├── go.sum ├── internal ├── cstr │ └── cstring.go └── syscall │ ├── bind.go │ ├── bpf.go │ ├── ioctl.go │ ├── perf.go │ ├── sendto.go │ ├── sockopt.go │ └── syscall.go ├── iterator.go ├── iterator_benchmark_test.go ├── iterator_test.go ├── kernel-changes.md ├── kernelsupport ├── api.go ├── arch.go ├── attach.go ├── doc.go ├── kernelsupport.go ├── kernelsupport_test.go ├── map.go ├── misc.go ├── program.go └── versions.go ├── map.go ├── map_abstract.go ├── map_array.go ├── map_array_of_maps.go ├── map_array_test.go ├── map_data.go ├── map_definition.go ├── map_hash.go ├── map_hash_of_maps.go ├── map_lpm_trie.go ├── map_lpm_trie_test.go ├── map_percpu_array.go ├── map_percpu_array_test.go ├── map_prog_array.go ├── map_queue.go ├── map_queue_test.go ├── map_stack.go ├── map_stack_test.go ├── map_xsk.go ├── mmap.go ├── perf ├── debugfs.go ├── doc.go └── perf_event.go ├── pin.go ├── prog_info.go ├── program.go ├── program_abstract.go ├── program_kprobe.go ├── program_socket_filter.go ├── program_tracepoint.go ├── program_xdp.go ├── tools └── check-go-mod.sh └── xsk.go /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | branches: 7 | - "master" 8 | pull_request: 9 | jobs: 10 | ci: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Install Go 14 | uses: actions/setup-go@v2 15 | with: 16 | go-version: 1.16.x 17 | - name: checkout code 18 | uses: actions/checkout@v2 19 | - name: Run linters 20 | uses: golangci/golangci-lint-action@v2 21 | with: 22 | version: v1.40.1 23 | - name: Run Unit tests 24 | run: go test ./... 25 | - name: Check go mod tidyness 26 | run: ./tools/check-go-mod.sh 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The GoBPFLD authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /bpfsys/doc.go: -------------------------------------------------------------------------------- 1 | // Package bpfsys contains low level functions related to syscalls and kernel interactions. 2 | // It is separated out into this package so the docs of the gobpfld package aren't cluttered. 3 | // It is recommended to use the more abstract/high level functions available in gobpfld if at all possible. 4 | // However this package is available for anyone how wants to use more low level functionality 5 | package bpfsys 6 | -------------------------------------------------------------------------------- /bpftypes/doc.go: -------------------------------------------------------------------------------- 1 | // Package bpftypes contains a lot of constants/enums, the package exists because all of these constants clutter the 2 | // generated documentation. By using a separate package the generated docs for the gobpfld package are cleaner. 3 | package bpftypes 4 | -------------------------------------------------------------------------------- /btf_test.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestBTFKind_String(t *testing.T) { 9 | for i := BTFKind(0); i < btfKindMax; i++ { 10 | if i.String() == "" { 11 | t.Fatalf("missing string translation for BPFKind %d", i) 12 | } 13 | } 14 | } 15 | 16 | func TestStringTbl_ParseSerializeSymmetry(t *testing.T) { 17 | type fields struct { 18 | Blob []byte 19 | } 20 | tests := []struct { 21 | name string 22 | fields fields 23 | }{ 24 | { 25 | name: "happy path", 26 | fields: fields{ 27 | Blob: []byte("\x00abc\x00def\x00ghi\x00"), 28 | }, 29 | }, 30 | { 31 | name: "no elements", 32 | fields: fields{ 33 | Blob: []byte(""), 34 | }, 35 | }, 36 | { 37 | name: "nil elem", 38 | fields: fields{ 39 | Blob: []byte("\x00"), 40 | }, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | cp := make([]byte, len(tt.fields.Blob)) 46 | copy(cp, tt.fields.Blob) 47 | 48 | st := StringTblFromBlob(tt.fields.Blob) 49 | st.Serialize() 50 | 51 | if !bytes.Equal(cp, st.btfStringBlob) { 52 | t.Fail() 53 | } 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cmd/examples/bpf_to_bpf/src/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - xdp.c | \ 3 | llc -march=bpf -filetype=obj -o xdp - -------------------------------------------------------------------------------- /cmd/examples/bpf_to_bpf/src/xdp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/bpf_to_bpf/src/xdp -------------------------------------------------------------------------------- /cmd/examples/icmp_pcap/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "embed" 6 | "fmt" 7 | "os" 8 | "syscall" 9 | 10 | "github.com/dylandreimerink/gobpfld" 11 | "github.com/dylandreimerink/gobpfld/bpftypes" 12 | ) 13 | 14 | //go:embed src/sockfilter 15 | var f embed.FS 16 | 17 | func main() { 18 | elfFileBytes, err := f.ReadFile("src/sockfilter") 19 | if err != nil { 20 | fmt.Fprintf(os.Stderr, "error opening ELF file: %s\n", err.Error()) 21 | os.Exit(1) 22 | } 23 | 24 | elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{}) 25 | if err != nil { 26 | fmt.Fprintf(os.Stderr, "error while reading ELF file: %s\n", err.Error()) 27 | os.Exit(1) 28 | } 29 | 30 | program := elf.Programs["sockfilter_prog"].(*gobpfld.ProgramSocketFilter) 31 | 32 | log, err := program.Load(gobpfld.ProgSKFilterLoadOpts{ 33 | VerifierLogLevel: bpftypes.BPFLogLevelBasic, 34 | VerifierLogSize: 1 << 20, 35 | }) 36 | fmt.Fprint(os.Stdout, log) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "program load: %s\n", err.Error()) 39 | os.Exit(1) 40 | } 41 | 42 | fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_ALL))) 43 | if err != nil { 44 | fmt.Fprintf(os.Stderr, "syscall socket: %s", err.Error()) 45 | os.Exit(1) 46 | } 47 | 48 | err = program.Attach(uintptr(fd)) 49 | if err != nil { 50 | fmt.Fprintf(os.Stderr, "socket attach: %s", err.Error()) 51 | os.Exit(1) 52 | } 53 | 54 | // Make a 32KB buffer 55 | buf := make([]byte, 1<<16) 56 | for { 57 | len, _, err := syscall.Recvfrom(fd, buf, 0) 58 | if err != nil { 59 | fmt.Fprintf(os.Stderr, "syscall recvfrom: %s", err.Error()) 60 | os.Exit(1) 61 | } 62 | 63 | fmt.Printf("% X\n", buf[:len]) 64 | } 65 | } 66 | 67 | // htons converts a short (uint16) from host-to-network byte order. 68 | func htons(i uint16) uint16 { 69 | return (i<<8)&0xff00 | i>>8 70 | } 71 | -------------------------------------------------------------------------------- /cmd/examples/icmp_pcap/src/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - sockfilter.c | \ 3 | llc -march=bpf -filetype=obj -o sockfilter - -------------------------------------------------------------------------------- /cmd/examples/icmp_pcap/src/sockfilter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/icmp_pcap/src/sockfilter -------------------------------------------------------------------------------- /cmd/examples/icmp_pcap/src/sockfilter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define SEC(NAME) __attribute__((section(NAME), used)) 8 | 9 | /* llvm builtin functions that eBPF C program may use to 10 | * emit BPF_LD_ABS and BPF_LD_IND instructions 11 | */ 12 | unsigned long long load_byte(void *skb, 13 | unsigned long long off) asm("llvm.bpf.load.byte"); 14 | unsigned long long load_half(void *skb, 15 | unsigned long long off) asm("llvm.bpf.load.half"); 16 | 17 | SEC("sock_filter") 18 | int sockfilter_prog(struct __sk_buff *skb) 19 | { 20 | __be16 h_proto = load_half(skb, offsetof(struct ethhdr, h_proto)); 21 | if( h_proto != ETH_P_IP ) { 22 | return 0; 23 | } 24 | 25 | int ip_proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); 26 | if( ip_proto != IPPROTO_ICMP ) { 27 | return 0; 28 | } 29 | 30 | return skb->len; 31 | } 32 | 33 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/examples/kprobe_execve_stats/bpf/.gitignore: -------------------------------------------------------------------------------- 1 | vmlinux.h -------------------------------------------------------------------------------- /cmd/examples/kprobe_execve_stats/bpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h && \ 3 | clang -D__TARGET_ARCH_x86 -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - tracex1_kern.c | \ 4 | llc -march=bpf -filetype=obj -o tracex1_kern - -------------------------------------------------------------------------------- /cmd/examples/kprobe_execve_stats/bpf/tracex1_kern: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/kprobe_execve_stats/bpf/tracex1_kern -------------------------------------------------------------------------------- /cmd/examples/kprobe_execve_stats/bpf/tracex1_kern.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | /* 4 | * bpf_map_lookup_elem 5 | * 6 | * Perform a lookup in *map* for an entry associated to *key*. 7 | * 8 | * Returns 9 | * Map value associated to *key*, or **NULL** if no entry was 10 | * found. 11 | */ 12 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; 13 | 14 | struct bpf_map_def { 15 | unsigned int type; 16 | unsigned int key_size; 17 | unsigned int value_size; 18 | unsigned int max_entries; 19 | unsigned int map_flags; 20 | }; 21 | 22 | #define SEC(name) __attribute__((section(name), used)) 23 | 24 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 25 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 26 | */ 27 | #ifndef lock_xadd 28 | #define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val)) 29 | #endif 30 | 31 | struct bpf_map_def SEC("maps") execve_stats = { 32 | .type = BPF_MAP_TYPE_ARRAY, 33 | .key_size = sizeof(__u32), 34 | .value_size = sizeof(__u64), 35 | .max_entries = 1, 36 | }; 37 | 38 | /* kprobe is NOT a stable ABI 39 | * kernel functions can be removed, renamed or completely change semantics. 40 | * Number of arguments and their positions can change, etc. 41 | * In such case this bpf+kprobe example will no longer be meaningful 42 | */ 43 | SEC("kprobe/__x64_sys_execve") 44 | int bpf_prog1(struct pt_regs *ctx) 45 | { 46 | __u64 *counter; 47 | __u32 key = 0; 48 | counter = bpf_map_lookup_elem(&execve_stats, &key); 49 | if (!counter) 50 | return 0; 51 | 52 | lock_xadd(counter, 1); 53 | 54 | return 0; 55 | } 56 | 57 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/examples/kprobe_execve_stats/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/dylandreimerink/gobpfld" 9 | "github.com/dylandreimerink/gobpfld/bpftypes" 10 | ) 11 | 12 | func main() { 13 | elf, err := os.Open("bpf/tracex1_kern") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | loadedElf, err := gobpfld.LoadProgramFromELF(elf, gobpfld.ELFParseSettings{ 19 | TruncateNames: true, 20 | }) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | stats := loadedElf.Maps["execve_stats"].(*gobpfld.ArrayMap) 26 | err = stats.Load() 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | program := loadedElf.Programs["bpf_prog1"].(*gobpfld.ProgramKProbe) 32 | 33 | log, err := program.Load(gobpfld.ProgKPLoadOpts{ 34 | VerifierLogLevel: bpftypes.BPFLogLevelVerbose, 35 | VerifierLogSize: 1 << 20, 36 | }) 37 | if err != nil { 38 | fmt.Println(log) 39 | panic(err) 40 | } 41 | 42 | err = program.Attach(gobpfld.ProgKPAttachOpts{}) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | defer func() { 48 | err = program.Detach() 49 | if err != nil { 50 | panic(err) 51 | } 52 | }() 53 | 54 | for i := 0; i < 10; i++ { 55 | time.Sleep(1 * time.Second) 56 | var val uint64 57 | err = stats.Get(0, &val) 58 | if err != nil { 59 | fmt.Println(err.Error()) 60 | } 61 | fmt.Println(val) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /cmd/examples/map_in_map/bpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - map_in_map_counter.c | \ 3 | llc -march=bpf -filetype=obj -o map_in_map_counter - -------------------------------------------------------------------------------- /cmd/examples/map_in_map/bpf/map_in_map_counter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/map_in_map/bpf/map_in_map_counter -------------------------------------------------------------------------------- /cmd/examples/map_in_map/bpf/map_in_map_counter.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | #include 4 | 5 | /* 6 | * bpf_map_lookup_elem 7 | * 8 | * Perform a lookup in *map* for an entry associated to *key*. 9 | * 10 | * Returns 11 | * Map value associated to *key*, or **NULL** if no entry was 12 | * found. 13 | */ 14 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; 15 | 16 | #define SEC(NAME) __attribute__((section(NAME), used)) 17 | 18 | struct bpf_map_def { 19 | unsigned int type; 20 | unsigned int key_size; 21 | unsigned int value_size; 22 | unsigned int max_entries; 23 | unsigned int map_flags; 24 | }; 25 | 26 | struct datarec { 27 | __u64 rx_packets; 28 | }; 29 | 30 | struct bpf_map_def SEC("maps") map_of_maps = { 31 | .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, 32 | .key_size = sizeof(__u32), 33 | .value_size = sizeof(__u32), 34 | .max_entries = 2, 35 | }; 36 | 37 | 38 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 39 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 40 | */ 41 | #ifndef lock_xadd 42 | #define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val)) 43 | #endif 44 | 45 | SEC("xdp") 46 | int xdp_stats1_func(struct xdp_md *ctx) 47 | { 48 | __u32 key = 0; 49 | 50 | // Lookup the pointer to the map inner map 51 | struct bpf_map_def *stats_map = bpf_map_lookup_elem(&map_of_maps, &key); 52 | if (!stats_map) { 53 | return XDP_ABORTED; 54 | } 55 | 56 | // Lookup datarec struct in inner map, we will use key 57 | struct datarec *rec; 58 | key = XDP_PASS; /* XDP_PASS = 2 */ 59 | 60 | rec = bpf_map_lookup_elem(stats_map, &key); 61 | if (!rec) 62 | return XDP_ABORTED; 63 | 64 | /* Multiple CPUs can access data record. Thus, the accounting needs to 65 | * use an atomic operation. 66 | */ 67 | lock_xadd(&rec->rx_packets, 1); 68 | 69 | return XDP_PASS; 70 | } 71 | 72 | char _license[] SEC("license") = "GPL"; 73 | -------------------------------------------------------------------------------- /cmd/examples/map_iterator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/dylandreimerink/gobpfld" 8 | "github.com/dylandreimerink/gobpfld/bpfsys" 9 | "github.com/dylandreimerink/gobpfld/bpftypes" 10 | ) 11 | 12 | const mapSize = 10 13 | 14 | func main() { 15 | testMap := &gobpfld.ArrayMap{ 16 | AbstractMap: gobpfld.AbstractMap{ 17 | Name: gobpfld.MustNewObjName("xdp_stats_map"), 18 | Definition: gobpfld.BPFMapDef{ 19 | Type: bpftypes.BPF_MAP_TYPE_ARRAY, 20 | KeySize: 4, // SizeOf(uint32) 21 | ValueSize: 8, // SizeOf(uint64) 22 | MaxEntries: mapSize + 1, 23 | }, 24 | }, 25 | } 26 | var ( 27 | key uint32 28 | value uint64 29 | ) 30 | 31 | err := testMap.Load() 32 | if err != nil { 33 | fmt.Fprintf(os.Stderr, "error while loading map: %s\n", err.Error()) 34 | os.Exit(1) 35 | } 36 | 37 | for i := uint32(1); i <= mapSize; i++ { 38 | val := uint64(i * 10) 39 | err = testMap.Set(i, &val, bpfsys.BPFMapElemAny) 40 | if err != nil { 41 | fmt.Fprintf(os.Stderr, "error while setting to map: %s\n", err.Error()) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | fmt.Println("[iterator foreach pointers]") 47 | fmt.Println("------------------------") 48 | 49 | iter := testMap.Iterator() 50 | err = gobpfld.MapIterForEach(iter, &key, &value, func(_, _ interface{}) error { 51 | fmt.Printf("key: %d, value: %d\n", key, value) 52 | return nil 53 | }) 54 | if err != nil { 55 | fmt.Fprintf(os.Stderr, "foreach ptr: %s\n", err.Error()) 56 | os.Exit(1) 57 | } 58 | 59 | fmt.Println("\n[iterator foreach values]") 60 | fmt.Println("------------------------") 61 | 62 | iter = testMap.Iterator() 63 | err = gobpfld.MapIterForEach(iter, uint32(0), uint64(0), func(k, v interface{}) error { 64 | kp := k.(*uint32) 65 | vp := v.(*uint64) 66 | fmt.Printf("key: %d, value: %d\n", *kp, *vp) 67 | return nil 68 | }) 69 | if err != nil { 70 | fmt.Fprintf(os.Stderr, "foreach value: %s\n", err.Error()) 71 | os.Exit(1) 72 | } 73 | 74 | fmt.Println("\n[iterator init + next]") 75 | fmt.Println("------------------------") 76 | 77 | iter = testMap.Iterator() 78 | err = iter.Init(&key, &value) 79 | if err != nil { 80 | fmt.Fprintf(os.Stderr, "error iter init: %s\n", err.Error()) 81 | os.Exit(1) 82 | } 83 | 84 | var updated bool 85 | for updated, err = iter.Next(); updated && err == nil; updated, err = iter.Next() { 86 | fmt.Printf("key: %v, value: %v\n", key, value) 87 | } 88 | if err != nil { 89 | fmt.Fprintf(os.Stderr, "error iter next: %s\n", err.Error()) 90 | os.Exit(1) 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /cmd/examples/per_cpu_map/bpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - percpu.c | \ 3 | llc -march=bpf -filetype=obj -o percpu - -------------------------------------------------------------------------------- /cmd/examples/per_cpu_map/bpf/percpu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/per_cpu_map/bpf/percpu -------------------------------------------------------------------------------- /cmd/examples/per_cpu_map/bpf/percpu.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* SPDX-License-Identifier: GPL-2.0 */ 4 | #include 5 | #include 6 | 7 | /* 8 | * bpf_map_lookup_elem 9 | * 10 | * Perform a lookup in *map* for an entry associated to *key*. 11 | * 12 | * Returns 13 | * Map value associated to *key*, or **NULL** if no entry was 14 | * found. 15 | */ 16 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; 17 | 18 | #define SEC(NAME) __attribute__((section(NAME), used)) 19 | 20 | struct bpf_map_def { 21 | unsigned int type; 22 | unsigned int key_size; 23 | unsigned int value_size; 24 | unsigned int max_entries; 25 | unsigned int map_flags; 26 | }; 27 | 28 | 29 | // Stats on packets keyed by protocol number 30 | struct bpf_map_def SEC("maps") cnt_map = { 31 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, 32 | .key_size = sizeof(__u32), 33 | .value_size = sizeof(__u64), 34 | .max_entries = 8, 35 | }; 36 | 37 | SEC("xdp") 38 | int percpumap_prog(struct xdp_md *ctx) 39 | { 40 | __u32* key = 0; 41 | __u64* val = bpf_map_lookup_elem(&cnt_map, &key); 42 | if( val != NULL ){ 43 | (*val)++; 44 | } 45 | 46 | return XDP_PASS; 47 | } 48 | 49 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/examples/tailcall/src/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - xdp.c | \ 3 | llc -march=bpf -filetype=obj -o xdp - -------------------------------------------------------------------------------- /cmd/examples/tailcall/src/xdp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/tailcall/src/xdp -------------------------------------------------------------------------------- /cmd/examples/test_xdp_program/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/dylandreimerink/gobpfld" 9 | "github.com/dylandreimerink/gobpfld/bpftypes" 10 | "github.com/dylandreimerink/gobpfld/ebpf" 11 | ) 12 | 13 | // To demonstrate the testing feature, a small program is used that blocks all IPv6 traffic. 14 | 15 | func main() { 16 | program := &gobpfld.ProgramXDP{ 17 | AbstractBPFProgram: gobpfld.AbstractBPFProgram{ 18 | Name: gobpfld.MustNewObjName("block_ipv6"), 19 | ProgramType: bpftypes.BPF_PROG_TYPE_XDP, 20 | License: "GPL", 21 | }, 22 | } 23 | 24 | asm := ` 25 | r0 = 2 # 2 = XDP_PASS 26 | r2 = *(u32 *)(r1 + 4) # r2 = xdp_md.data_end 27 | r1 = *(u32 *)(r1 + 0) # r1 = xdp_md.data 28 | r1 += 12 # EthType is at offset 12 29 | r3 = r1 # Use r3 for bounds checking 30 | r3 += 2 # Add 2 since we want to read 2 bytes (u16) 31 | if r3 > r2 goto return # If there are less than 14 bytes, pass the packet (bounds check) 32 | r1 = *(u16 *)(r1 + 0) # Read EthType from frame 33 | if r1 != 0xDD86 goto return # If 86DD = IPv6, reversed due to network byte order 34 | r0 = 1 # 1 = XDP_DROP 35 | return: 36 | exit 37 | ` 38 | 39 | inst, err := ebpf.AssemblyToInstructions("inline-asm", strings.NewReader(asm)) 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | program.Instructions = ebpf.MustEncode(inst) 45 | 46 | log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 47 | VerifierLogLevel: bpftypes.BPFLogLevelBasic, 48 | }) 49 | 50 | fmt.Printf("BPF Verifier log:\n%s\n", log) 51 | 52 | if err != nil { 53 | err2 := program.DecodeToReader(os.Stdout) 54 | if err2 != nil { 55 | fmt.Fprintf(os.Stderr, "error while decoding program: %s\n", err.Error()) 56 | } 57 | fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error()) 58 | os.Exit(1) 59 | } 60 | 61 | ret, err := program.XDPTestProgram(gobpfld.TestXDPProgSettings{ 62 | Data: []byte{ 63 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // SRC MAC 64 | 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, // DST MAC 65 | 0x86, 0xDD, // EthType = IPv6 66 | 0x00, 0x00, 67 | }, 68 | Repeat: 1000, 69 | }) 70 | if err != nil { 71 | fmt.Fprintf(os.Stderr, "error while testing program: %s\n", err.Error()) 72 | os.Exit(1) 73 | } 74 | 75 | fmt.Fprintf(os.Stdout, "Duration: %d, return value: %d\n", ret.Duration, ret.ReturnValue) 76 | 77 | ret, err = program.XDPTestProgram(gobpfld.TestXDPProgSettings{ 78 | Data: []byte{ 79 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // SRC MAC 80 | 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, // DST MAC 81 | 0x08, 0x00, // EthType = IPv4 82 | 0x00, 0x00, 83 | }, 84 | Repeat: 1000, 85 | }) 86 | if err != nil { 87 | fmt.Fprintf(os.Stderr, "error while testing program: %s\n", err.Error()) 88 | os.Exit(1) 89 | } 90 | 91 | fmt.Fprintf(os.Stdout, "Duration: %d, return value: %d\n", ret.Duration, ret.ReturnValue) 92 | } 93 | -------------------------------------------------------------------------------- /cmd/examples/udp_socket_filter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "embed" 7 | "fmt" 8 | "net" 9 | "os" 10 | 11 | "github.com/dylandreimerink/gobpfld" 12 | "github.com/dylandreimerink/gobpfld/bpftypes" 13 | ) 14 | 15 | //go:embed src/sockfilter 16 | var f embed.FS 17 | 18 | func main() { 19 | elfFileBytes, err := f.ReadFile("src/sockfilter") 20 | if err != nil { 21 | fmt.Fprintf(os.Stderr, "error opening ELF file: %s\n", err.Error()) 22 | os.Exit(1) 23 | } 24 | 25 | elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{}) 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "error while reading ELF file: %s\n", err.Error()) 28 | os.Exit(1) 29 | } 30 | 31 | program := elf.Programs["sockfilter_prog"].(*gobpfld.ProgramSocketFilter) 32 | 33 | log, err := program.Load(gobpfld.ProgSKFilterLoadOpts{ 34 | VerifierLogLevel: bpftypes.BPFLogLevelBasic, 35 | VerifierLogSize: 1 << 20, 36 | }) 37 | fmt.Fprint(os.Stdout, log) 38 | if err != nil { 39 | fmt.Fprintf(os.Stderr, "program load: %s\n", err.Error()) 40 | os.Exit(1) 41 | } 42 | 43 | lc := net.ListenConfig{ 44 | Control: program.SocketAttachControlFunc, 45 | } 46 | conn, err := lc.ListenPacket(context.Background(), "udp", ":3000") 47 | if err != nil { 48 | fmt.Fprintf(os.Stderr, "listen packet: %s\n", err.Error()) 49 | os.Exit(1) 50 | } 51 | 52 | // Make a 32KB buffer 53 | buf := make([]byte, 1<<16) 54 | for { 55 | len, _, err := conn.ReadFrom(buf) 56 | if err != nil { 57 | fmt.Fprintf(os.Stderr, "conn read from: %s", err.Error()) 58 | os.Exit(1) 59 | } 60 | 61 | fmt.Printf("% X\n", buf[:len]) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /cmd/examples/udp_socket_filter/src/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - sockfilter.c | \ 3 | llc -march=bpf -filetype=obj -o sockfilter - -------------------------------------------------------------------------------- /cmd/examples/udp_socket_filter/src/sockfilter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/udp_socket_filter/src/sockfilter -------------------------------------------------------------------------------- /cmd/examples/udp_socket_filter/src/sockfilter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define SEC(NAME) __attribute__((section(NAME), used)) 8 | 9 | /* llvm builtin functions that eBPF C program may use to 10 | * emit BPF_LD_ABS and BPF_LD_IND instructions 11 | */ 12 | unsigned long long load_byte(void *skb, 13 | unsigned long long off) asm("llvm.bpf.load.byte"); 14 | unsigned long long load_half(void *skb, 15 | unsigned long long off) asm("llvm.bpf.load.half"); 16 | unsigned long long load_word(void *skb, 17 | unsigned long long off) asm("llvm.bpf.load.word"); 18 | 19 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 20 | (void *) BPF_FUNC_trace_printk; 21 | 22 | /* Helper macro to print out debug messages */ 23 | #define bpf_printk(fmt, ...) \ 24 | ({ \ 25 | char ____fmt[] = fmt; \ 26 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 27 | ##__VA_ARGS__); \ 28 | }) 29 | 30 | SEC("sock_filter") 31 | int sockfilter_prog(struct __sk_buff *skb) 32 | { 33 | // Use SKF_NET_OFF to access the IP header which is needed since the skb only contains 34 | // the L4 (UDP) header since the associated socket is a UDP socket 35 | __be32 dst_addr = load_word(skb, SKF_NET_OFF + offsetof(struct iphdr, daddr)); 36 | 37 | // drop traffic unless it is to 127.0.0.1 38 | const __be32 localhost = 0x7F000001; 39 | if( dst_addr != localhost ) { 40 | return 0; 41 | } 42 | 43 | int udp_len = load_half(skb, offsetof(struct udphdr, len)); 44 | // bpf_printk("size: %d", sizeof(struct udphdr) + udp_len); 45 | return sizeof(struct udphdr) + udp_len; 46 | } 47 | 48 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/examples/uprobe_bash_stats/bpf/.gitignore: -------------------------------------------------------------------------------- 1 | vmlinux.h -------------------------------------------------------------------------------- /cmd/examples/uprobe_bash_stats/bpf/bash_stats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/uprobe_bash_stats/bpf/bash_stats -------------------------------------------------------------------------------- /cmd/examples/uprobe_bash_stats/bpf/bash_stats.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | /* 4 | * bpf_map_lookup_elem 5 | * 6 | * Perform a lookup in *map* for an entry associated to *key*. 7 | * 8 | * Returns 9 | * Map value associated to *key*, or **NULL** if no entry was 10 | * found. 11 | */ 12 | static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; 13 | 14 | struct bpf_map_def { 15 | unsigned int type; 16 | unsigned int key_size; 17 | unsigned int value_size; 18 | unsigned int max_entries; 19 | unsigned int map_flags; 20 | }; 21 | 22 | #define SEC(name) __attribute__((section(name), used)) 23 | 24 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 25 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 26 | */ 27 | #ifndef lock_xadd 28 | #define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val)) 29 | #endif 30 | 31 | struct bpf_map_def SEC("maps") bash_stats = { 32 | .type = BPF_MAP_TYPE_ARRAY, 33 | .key_size = sizeof(__u32), 34 | .value_size = sizeof(__u64), 35 | .max_entries = 1, 36 | }; 37 | 38 | /* kprobe is NOT a stable ABI 39 | * kernel functions can be removed, renamed or completely change semantics. 40 | * Number of arguments and their positions can change, etc. 41 | * In such case this bpf+kprobe example will no longer be meaningful 42 | */ 43 | SEC("uprobe/bin/bash/0x030360") 44 | int bpf_prog1(struct pt_regs *ctx) 45 | { 46 | __u64 *counter; 47 | __u32 key = 0; 48 | counter = bpf_map_lookup_elem(&bash_stats, &key); 49 | if (!counter) 50 | return 0; 51 | 52 | lock_xadd(counter, 1); 53 | 54 | return 0; 55 | } 56 | 57 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/examples/uprobe_bash_stats/bpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h && \ 3 | clang -D__TARGET_ARCH_x86 -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - bash_stats.c | \ 4 | llc -march=bpf -filetype=obj -o bash_stats - -------------------------------------------------------------------------------- /cmd/examples/uprobe_bash_stats/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/dylandreimerink/gobpfld" 9 | "github.com/dylandreimerink/gobpfld/bpftypes" 10 | ) 11 | 12 | func main() { 13 | elf, err := os.Open("bpf/bash_stats") 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | loadedElf, err := gobpfld.LoadProgramFromELF(elf, gobpfld.ELFParseSettings{ 19 | TruncateNames: true, 20 | }) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | stats := loadedElf.Maps["bash_stats"].(*gobpfld.ArrayMap) 26 | err = stats.Load() 27 | if err != nil { 28 | panic(err) 29 | } 30 | 31 | program := loadedElf.Programs["bpf_prog1"].(*gobpfld.ProgramKProbe) 32 | 33 | log, err := program.Load(gobpfld.ProgKPLoadOpts{ 34 | VerifierLogLevel: bpftypes.BPFLogLevelVerbose, 35 | VerifierLogSize: 1 << 20, 36 | }) 37 | if err != nil { 38 | fmt.Println(log) 39 | panic(err) 40 | } 41 | 42 | fmt.Println("attaching") 43 | 44 | err = program.Attach(gobpfld.ProgKPAttachOpts{ 45 | Event: "bash_trace", 46 | }) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | fmt.Println("attached") 52 | 53 | defer func() { 54 | err = program.Detach() 55 | if err != nil { 56 | panic(err) 57 | } 58 | }() 59 | 60 | for i := 0; i < 10; i++ { 61 | time.Sleep(1 * time.Second) 62 | var val uint64 63 | err = stats.Get(0, &val) 64 | if err != nil { 65 | fmt.Println(err.Error()) 66 | } 67 | fmt.Println(val) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cmd/examples/xdp_stats/bpf/basic03_map_counter: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/examples/xdp_stats/bpf/basic03_map_counter -------------------------------------------------------------------------------- /cmd/examples/xdp_stats/bpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -S -target bpf -Wall -O2 -emit-llvm -c -g -I/usr/include -o - basic03_map_counter.c | \ 3 | llc -march=bpf -filetype=obj -o basic03_map_counter - -------------------------------------------------------------------------------- /cmd/examples/xdp_stats/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "embed" 6 | "fmt" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/dylandreimerink/gobpfld" 12 | "github.com/dylandreimerink/gobpfld/bpftypes" 13 | "golang.org/x/sys/unix" 14 | 15 | _ "embed" 16 | ) 17 | 18 | //go:embed bpf/basic03_map_counter 19 | var f embed.FS 20 | 21 | // This example command is a pure go replacement for the userpace program of the Basic03 program from 22 | // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter 23 | // This example has no options but does demonstrate program loading from ELF, attaching to a interface, and interacting with a map 24 | 25 | func main() { 26 | elfFileBytes, err := f.ReadFile("bpf/basic03_map_counter") 27 | if err != nil { 28 | fmt.Fprintf(os.Stderr, "error opening ELF file: %s\n", err.Error()) 29 | os.Exit(1) 30 | } 31 | 32 | elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{}) 33 | if err != nil { 34 | fmt.Fprintf(os.Stderr, "error while reading ELF file: %s\n", err.Error()) 35 | os.Exit(1) 36 | } 37 | 38 | program := elf.Programs["xdp_stats1_func"].(*gobpfld.ProgramXDP) 39 | 40 | // All maps loaded from elf files are BPFGenericMaps 41 | statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap) 42 | 43 | log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 44 | VerifierLogLevel: bpftypes.BPFLogLevelBasic, 45 | }) 46 | 47 | fmt.Printf("BPF Verifier log:\n%s\n", log) 48 | 49 | if err != nil { 50 | fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error()) 51 | os.Exit(1) 52 | } 53 | 54 | sigChan := make(chan os.Signal, 1) 55 | signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT) 56 | 57 | err = program.Attach(gobpfld.ProgXDPAttachOpts{ 58 | InterfaceName: "lo", 59 | Replace: true, 60 | }) 61 | 62 | if err != nil { 63 | fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error()) 64 | os.Exit(1) 65 | } 66 | 67 | detach := func() { 68 | err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{ 69 | All: true, 70 | }) 71 | if err != nil { 72 | fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error()) 73 | os.Exit(1) 74 | } 75 | } 76 | 77 | ticker := time.Tick(1 * time.Second) 78 | for { 79 | select { 80 | case <-ticker: 81 | // The key is 2 since the program puts stats in the XDP_PASS key which has value 2 82 | // Tho this is specific to the XDP program we are using as an example. 83 | var value int64 84 | err = statsMap.Get(2, &value) 85 | if err != nil { 86 | fmt.Fprintf(os.Stderr, "error while getting stats from map: %s\n", err.Error()) 87 | detach() 88 | os.Exit(1) 89 | } 90 | 91 | fmt.Printf("%d packets were processed\n", value) 92 | 93 | case <-sigChan: 94 | fmt.Println("Detaching XPD program and stopping") 95 | 96 | detach() 97 | 98 | os.Exit(0) 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /cmd/testsuite/.gitignore: -------------------------------------------------------------------------------- 1 | # These are temporary until we can download them from the bpfci repo 2 | bzImage 3 | initrd.gz 4 | gobpfld-test-results -------------------------------------------------------------------------------- /cmd/testsuite/Readme.md: -------------------------------------------------------------------------------- 1 | # Test suite 2 | 3 | This test suite attempts to test GoBPFLD, which consists of a few different kinds of tests: 4 | - Integration tests - Execute a series of common BPF scenarios to see if we crash or get unexpected errors. This includes the loading of all sorts of programs and maps. We want a much coverage as possible. 5 | - Compatibility tests - Execute the same tests on multiple architectures and kernel versions. GoBPFLD should be able to work on any architecture, and give proper warnings if features are not available on older kernels. 6 | - Fuzzing - Call GoBPFLD with semi-random input in an attempt to break it. GoBPFLD should never panic in bad input, just return errors which users can handle. Focus areas are ELF parsing and decoding(including BTF, Program, and Map). 7 | 8 | 9 | ## TODO / The plan 10 | 11 | * Provide pre-compiled kernelBz + initrd images for a few kernel version (before and after notable BPF changes) via bpfci repo/project 12 | * Provide pre-compiled kernelBz + initrd images for x86_64, i386, arm64 and RISC-V (maybe just the newest kernel versions for now) via bpfci repo/project 13 | * Create `go test` compatible eBPF/XDP tests 14 | * Create go program which automates environment setup and tests 15 | * Pick which tests to run via the sub-commands 16 | * Execute C/BPF recompilation(not needed if .o files are already committed) 17 | * Execute go test/go build command for tests (add flag to only compile a specific test(for during debugging)) 18 | * (cross) compile for x86_64, i386, arm64 and RISC-V if requested (command line flag) 19 | * Compile with coverage if requested (command line flag) 20 | * Compile with race condition checking if requested (command line flag) 21 | * Compile with profiling options if requested (command line flags) 22 | * Compile with JSON output (always, for automatic test checking) 23 | * Run tests in QEMU for arch under test 24 | * Make a mountable disk image on host (.qcow2 or raw) 25 | * Place `run.sh` or `exec.sh` (known name called by bpfci `init` on load) in image 26 | * script will execute generated test executable with requested cli arguments 27 | * All outputs(exit code, stdout, stderr, coverage, profiles) are written to the mounted image 28 | * After running tests, make the script poweroff the VM to indicate to the host we are done 29 | * Place the unit test binary on the image 30 | * Start VM which will start the tests once ready 31 | * Mount or unpack the disk image and extract the test results 32 | * Post process results 33 | * Evaluate test results(json output) and display human readable errors. 34 | * Generate HTML coverage reports(include raw data and HTML version) 35 | * Generate SVGs of profiles 36 | * Make .zip/.tar.gz of all results in run(may be multiple kernel versions / architectures) 37 | * Display short and detailed test report (in markdown to be included in git) 38 | * Run fuzzer (TODO research how to go about this) -------------------------------------------------------------------------------- /cmd/testsuite/integration/doc.go: -------------------------------------------------------------------------------- 1 | //go:build bpftests 2 | // +build bpftests 3 | 4 | // package integration contains integration tests, which test full scenarios, an thus are expected to fail 5 | // more frequently. 6 | package integration 7 | -------------------------------------------------------------------------------- /cmd/testsuite/integration/ebpf/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # xdp_stats_test 4 | clang -target bpf -Wall -O2 -g -c xdp_stats_test.c -I/usr/include -o xdp_stats_test 5 | # global_data_test 6 | clang -target bpf -Wall -O2 -g -c global_data_test.c -I/usr/include -o global_data_test -------------------------------------------------------------------------------- /cmd/testsuite/integration/ebpf/global_data_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/testsuite/integration/ebpf/global_data_test -------------------------------------------------------------------------------- /cmd/testsuite/integration/ebpf/global_data_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SEC(NAME) __attribute__((section(NAME), used)) 5 | 6 | static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; 7 | 8 | struct bpf_map_def { 9 | unsigned int type; 10 | unsigned int key_size; 11 | unsigned int value_size; 12 | unsigned int max_entries; 13 | unsigned int map_flags; 14 | }; 15 | 16 | struct map_val { 17 | __u64 cnt; 18 | }; 19 | 20 | struct bpf_map_def SEC("maps") result_number = { 21 | .type = BPF_MAP_TYPE_ARRAY, 22 | .key_size = sizeof(__u32), 23 | .value_size = sizeof(__u64), 24 | .max_entries = 11, 25 | }; 26 | 27 | struct bpf_map_def SEC("maps") result_string = { 28 | .type = BPF_MAP_TYPE_ARRAY, 29 | .key_size = sizeof(__u32), 30 | .value_size = 32, 31 | .max_entries = 5, 32 | }; 33 | 34 | struct foo { 35 | __u8 a; 36 | __u32 b; 37 | __u64 c; 38 | }; 39 | 40 | struct bpf_map_def SEC("maps") result_struct = { 41 | .type = BPF_MAP_TYPE_ARRAY, 42 | .key_size = sizeof(__u32), 43 | .value_size = sizeof(struct foo), 44 | .max_entries = 5, 45 | }; 46 | 47 | 48 | /* Relocation tests for __u64s. */ 49 | static __u64 num0; 50 | static __u64 num1 = 42; 51 | static const __u64 num2 = 24; 52 | static __u64 num3 = 0; 53 | static __u64 num4 = 0xffeeff; 54 | static const __u64 num5 = 0xabab; 55 | static const __u64 num6 = 0xab; 56 | 57 | /* Relocation tests for strings. */ 58 | static const char str0[32] = "abcdefghijklmnopqrstuvwxyz"; 59 | static char str1[32] = "abcdefghijklmnopqrstuvwxyz"; 60 | static char str2[32]; 61 | 62 | /* Relocation tests for structs. */ 63 | static const struct foo struct0 = { 64 | .a = 42, 65 | .b = 0xfefeefef, 66 | .c = 0x1111111111111111ULL, 67 | }; 68 | static struct foo struct1; 69 | static const struct foo struct2; 70 | static struct foo struct3 = { 71 | .a = 41, 72 | .b = 0xeeeeefef, 73 | .c = 0x2111111111111111ULL, 74 | }; 75 | 76 | #define test_reloc(map, num, var) \ 77 | do { \ 78 | __u32 key = num; \ 79 | bpf_map_update_elem(&result_##map, &key, var, 0); \ 80 | } while (0) 81 | 82 | SEC("xdp") 83 | int load_static_data(struct xdp_md *ctx) 84 | { 85 | static const __u64 bar = ~0; 86 | 87 | test_reloc(number, 0, &num0); 88 | test_reloc(number, 1, &num1); 89 | test_reloc(number, 2, &num2); 90 | test_reloc(number, 3, &num3); 91 | test_reloc(number, 4, &num4); 92 | test_reloc(number, 5, &num5); 93 | num4 = 1234; 94 | test_reloc(number, 6, &num4); 95 | test_reloc(number, 7, &num0); 96 | test_reloc(number, 8, &num6); 97 | 98 | test_reloc(string, 0, str0); 99 | test_reloc(string, 1, str1); 100 | test_reloc(string, 2, str2); 101 | str1[5] = 'x'; 102 | test_reloc(string, 3, str1); 103 | __builtin_memcpy(&str2[2], "hello", sizeof("hello")); 104 | test_reloc(string, 4, str2); 105 | 106 | test_reloc(struct, 0, &struct0); 107 | test_reloc(struct, 1, &struct1); 108 | test_reloc(struct, 2, &struct2); 109 | test_reloc(struct, 3, &struct3); 110 | 111 | test_reloc(number, 9, &struct0.c); 112 | test_reloc(number, 10, &bar); 113 | 114 | return XDP_PASS; 115 | } 116 | 117 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /cmd/testsuite/integration/ebpf/xdp_stats_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylandreimerink/gobpfld/e79c330ad60864f992421ed101ded72a26bd699c/cmd/testsuite/integration/ebpf/xdp_stats_test -------------------------------------------------------------------------------- /cmd/testsuite/integration/xdp_stats_test.go: -------------------------------------------------------------------------------- 1 | //go:build bpftests 2 | // +build bpftests 3 | 4 | package integration 5 | 6 | import ( 7 | "bytes" 8 | "net" 9 | "testing" 10 | "time" 11 | 12 | "github.com/dylandreimerink/gobpfld" 13 | "github.com/dylandreimerink/gobpfld/bpftypes" 14 | ) 15 | 16 | // This example command is a pure go replacement for the userpace program of the Basic03 program from 17 | // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter 18 | // This example has no options but does demonstrate program loading from ELF, attaching to a interface, and interacting with a map 19 | func TestIntegrationXDPstats(t *testing.T) { 20 | elfFileBytes, err := ebpf.ReadFile("ebpf/xdp_stats_test") 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{}) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | program := elf.Programs["xdp_stats1_func"].(*gobpfld.ProgramXDP) 31 | 32 | // All maps loaded from elf files are BPFGenericMaps 33 | statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap) 34 | 35 | log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 36 | VerifierLogLevel: bpftypes.BPFLogLevelBasic, 37 | }) 38 | if err != nil { 39 | t.Log(log) 40 | t.Fatal(err) 41 | } 42 | 43 | err = program.Attach(gobpfld.ProgXDPAttachOpts{ 44 | InterfaceName: "lo", 45 | Replace: true, 46 | }) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | detach := func() { 52 | err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{ 53 | All: true, 54 | }) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | } 59 | defer detach() 60 | 61 | udpConn, err := net.Dial("udp", "127.0.0.1:123") 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | 66 | // Write some bogus UDP packets to localhost to generate traffic 67 | ticker := time.Tick(1 * time.Second) 68 | endTimer := time.NewTimer(5 * time.Second) 69 | stop := false 70 | for !stop { 71 | select { 72 | case <-ticker: 73 | udpConn.Write([]byte("Hello World")) 74 | 75 | case <-endTimer.C: 76 | stop = true 77 | } 78 | } 79 | 80 | // The key is 2 since the program puts stats in the XDP_PASS key which has value 2 81 | // Tho this is specific to the XDP program we are using as an example. 82 | var value int64 83 | err = statsMap.Get(2, &value) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | if value == 0 { 89 | t.Fatal("Expected at least one packet") 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ebpf/add.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Add32)(nil) 6 | 7 | type Add32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Add32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_ADD, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Add32) String() string { 19 | return fmt.Sprintf("w%s += %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Add64)(nil) 23 | 24 | type Add64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Add64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_ADD, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Add64) String() string { 36 | return fmt.Sprintf("r%s += %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Add32Register)(nil) 40 | 41 | type Add32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Add32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_ADD, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Add32Register) String() string { 53 | return fmt.Sprintf("w%s += w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Add64Register)(nil) 57 | 58 | type Add64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Add64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_ADD, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Add64Register) String() string { 70 | return fmt.Sprintf("r%s += r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/and.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*And32)(nil) 6 | 7 | type And32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *And32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_AND, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *And32) String() string { 19 | return fmt.Sprintf("w%s &= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*And64)(nil) 23 | 24 | type And64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *And64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_AND, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *And64) String() string { 36 | return fmt.Sprintf("r%s &= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*And32Register)(nil) 40 | 41 | type And32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *And32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_AND, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *And32Register) String() string { 53 | return fmt.Sprintf("w%s &= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*And64Register)(nil) 57 | 58 | type And64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *And64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_AND, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *And64Register) String() string { 70 | return fmt.Sprintf("r%s &= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/arsh.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*ARSH32)(nil) 6 | 7 | type ARSH32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *ARSH32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_ARSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *ARSH32) String() string { 19 | return fmt.Sprintf("w%s s>>= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*ARSH64)(nil) 23 | 24 | type ARSH64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *ARSH64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_ARSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *ARSH64) String() string { 36 | return fmt.Sprintf("r%s s>>= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*ARSH32Register)(nil) 40 | 41 | type ARSH32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *ARSH32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_ARSH, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *ARSH32Register) String() string { 53 | return fmt.Sprintf("w%s s>>= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*ARSH64Register)(nil) 57 | 58 | type ARSH64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *ARSH64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_ARSH, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *ARSH64Register) String() string { 70 | return fmt.Sprintf("r%s s>>= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/asm_test.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import ( 4 | "bytes" 5 | "embed" 6 | "testing" 7 | ) 8 | 9 | //go:embed asm_test.bpfasm 10 | var assembly embed.FS 11 | 12 | const filename = "asm_test.bpfasm" 13 | 14 | // This test ensures that the format accepted by the dissasembler matches the output of decoded instructions. 15 | // This test doesn't include dissasembly specific features like labels and number formatting. 16 | func TestDecodeEncodeSymmetry(t *testing.T) { 17 | fileContents, err := assembly.ReadFile(filename) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | 22 | disInst, err := AssemblyToInstructions(filename, bytes.NewReader(fileContents)) 23 | if err != nil { 24 | t.Error(err) 25 | } 26 | 27 | var disassembled string 28 | for _, inst := range disInst { 29 | disassembled += inst.String() + "\n" 30 | } 31 | 32 | encoded, err := Encode(disInst) 33 | if err != nil { 34 | t.Error(err) 35 | } 36 | 37 | decInst, err := Decode(encoded) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | 42 | var decoded string 43 | for _, inst := range decInst { 44 | decoded += inst.String() + "\n" 45 | } 46 | 47 | if string(fileContents) != decoded { 48 | t.Error("Encoding and decoding not symetric") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ebpf/byteorder.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import ( 4 | "encoding/binary" 5 | "unsafe" 6 | ) 7 | 8 | // multi-byte constants in eBPF programs must always be in network order. Since constants in golang are represented in 9 | // the byte order of the host this may cause issues. The functions in this file automatically detect the endianness 10 | // of the host at runtime and converts them to or from network byte order. 11 | 12 | var nativeEndian binary.ByteOrder 13 | 14 | func getNativeEndianness() binary.ByteOrder { 15 | if nativeEndian != nil { 16 | return nativeEndian 17 | } 18 | 19 | buf := [2]byte{} 20 | *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 21 | 22 | switch buf { 23 | case [2]byte{0xCD, 0xAB}: 24 | nativeEndian = binary.LittleEndian 25 | case [2]byte{0xAB, 0xCD}: 26 | nativeEndian = binary.BigEndian 27 | default: 28 | panic("Could not determine native endianness.") 29 | } 30 | 31 | return nativeEndian 32 | } 33 | 34 | // HtonU16 converts a uint16 from host-to-network byte order. 35 | func HtonU16(i uint16) uint16 { 36 | b := make([]byte, 2) 37 | getNativeEndianness().PutUint16(b, i) 38 | return binary.BigEndian.Uint16(b) 39 | } 40 | 41 | // Hton16 converts a int16 from host-to-network byte order. 42 | func Hton16(u int16) int16 { 43 | return int16(HtonU16(uint16(u))) 44 | } 45 | 46 | // HtonU32 converts a uint32 from host-to-network byte order. 47 | func HtonU32(i uint32) uint32 { 48 | b := make([]byte, 4) 49 | getNativeEndianness().PutUint32(b, i) 50 | return binary.BigEndian.Uint32(b) 51 | } 52 | 53 | // Hton32 converts a int32 from host-to-network byte order. 54 | func Hton32(u int32) int32 { 55 | return int32(HtonU32(uint32(u))) 56 | } 57 | 58 | // HtonU64 converts a uint64 from host-to-network byte order. 59 | func HtonU64(i uint64) uint64 { 60 | b := make([]byte, 8) 61 | getNativeEndianness().PutUint64(b, i) 62 | return binary.BigEndian.Uint64(b) 63 | } 64 | 65 | // Hton64 converts a int64 from host-to-network byte order. 66 | func Hton64(u int64) int64 { 67 | return int64(HtonU64(uint64(u))) 68 | } 69 | 70 | // NtohU16 converts a uint16 from network-to-host byte order. 71 | func NtohU16(i uint16) uint16 { 72 | b := make([]byte, 2) 73 | binary.BigEndian.PutUint16(b, i) 74 | return getNativeEndianness().Uint16(b) 75 | } 76 | 77 | // Ntoh16 converts a int16 from host-to-network byte order. 78 | func Ntoh16(u int16) int16 { 79 | return int16(NtohU16(uint16(u))) 80 | } 81 | 82 | // NtohU32 converts a uint32 from network-to-host byte order. 83 | func NtohU32(i uint32) uint32 { 84 | b := make([]byte, 4) 85 | binary.BigEndian.PutUint32(b, i) 86 | return getNativeEndianness().Uint32(b) 87 | } 88 | 89 | // Ntoh32 converts a int32 from host-to-network byte order. 90 | func Ntoh32(u int32) int32 { 91 | return int32(NtohU32(uint32(u))) 92 | } 93 | 94 | // NtohU64 converts a uint64 from network-to-host byte order. 95 | func NtohU64(i uint64) uint64 { 96 | b := make([]byte, 8) 97 | binary.BigEndian.PutUint64(b, i) 98 | return getNativeEndianness().Uint64(b) 99 | } 100 | 101 | // Ntoh64 converts a int64 from host-to-network byte order. 102 | func Ntoh64(u int64) int64 { 103 | return int64(NtohU64(uint64(u))) 104 | } 105 | -------------------------------------------------------------------------------- /ebpf/call_bpf.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*CallBPF)(nil) 7 | _ Jumper = (*CallBPF)(nil) 8 | ) 9 | 10 | type CallBPF struct { 11 | Offset int32 12 | } 13 | 14 | func (c *CallBPF) Raw() ([]RawInstruction, error) { 15 | return []RawInstruction{ 16 | {Op: BPF_CALL | BPF_JMP, Reg: NewReg(PSEUDO_CALL, 0), Imm: c.Offset}, 17 | }, nil 18 | } 19 | 20 | func (c *CallBPF) String() string { 21 | return fmt.Sprintf("call %+d", c.Offset) 22 | } 23 | 24 | func (c *CallBPF) SetJumpTarget(relAddr int16) { 25 | c.Offset = int32(relAddr) 26 | } 27 | -------------------------------------------------------------------------------- /ebpf/div.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Div32)(nil) 6 | 7 | type Div32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Div32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_DIV, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Div32) String() string { 19 | return fmt.Sprintf("w%s /= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Div64)(nil) 23 | 24 | type Div64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Div64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_DIV, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Div64) String() string { 36 | return fmt.Sprintf("r%s /= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Div32Register)(nil) 40 | 41 | type Div32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Div32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_DIV, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Div32Register) String() string { 53 | return fmt.Sprintf("w%s /= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Div64Register)(nil) 57 | 58 | type Div64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Div64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_DIV, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Div64Register) String() string { 70 | return fmt.Sprintf("r%s /= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/doc.go: -------------------------------------------------------------------------------- 1 | // Package ebpf contains all types and constants to decode, encode, and generate eBPF bytecode in go. 2 | // This package is inspired by the classic bpf package https://pkg.go.dev/golang.org/x/net/bpf 3 | package ebpf 4 | -------------------------------------------------------------------------------- /ebpf/end.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*End16ToLE)(nil) 6 | 7 | type End16ToLE struct { 8 | Dest Register 9 | } 10 | 11 | func (a *End16ToLE) Raw() ([]RawInstruction, error) { 12 | return []RawInstruction{ 13 | {Op: BPF_ALU | BPF_END | BPF_TO_LE, Reg: NewReg(0, a.Dest), Imm: 16}, 14 | }, nil 15 | } 16 | 17 | func (a *End16ToLE) String() string { 18 | return fmt.Sprintf("r%s = le16 r%s", a.Dest, a.Dest) 19 | } 20 | 21 | var _ Instruction = (*End32ToLE)(nil) 22 | 23 | type End32ToLE struct { 24 | Dest Register 25 | } 26 | 27 | func (a *End32ToLE) Raw() ([]RawInstruction, error) { 28 | return []RawInstruction{ 29 | {Op: BPF_ALU | BPF_END | BPF_TO_LE, Reg: NewReg(0, a.Dest), Imm: 32}, 30 | }, nil 31 | } 32 | 33 | func (a *End32ToLE) String() string { 34 | return fmt.Sprintf("r%s = le32 r%s", a.Dest, a.Dest) 35 | } 36 | 37 | var _ Instruction = (*End64ToLE)(nil) 38 | 39 | type End64ToLE struct { 40 | Dest Register 41 | } 42 | 43 | func (a *End64ToLE) Raw() ([]RawInstruction, error) { 44 | return []RawInstruction{ 45 | {Op: BPF_ALU | BPF_END | BPF_TO_LE, Reg: NewReg(0, a.Dest), Imm: 64}, 46 | }, nil 47 | } 48 | 49 | func (a *End64ToLE) String() string { 50 | return fmt.Sprintf("r%s = le64 r%s", a.Dest, a.Dest) 51 | } 52 | 53 | var _ Instruction = (*End16ToBE)(nil) 54 | 55 | type End16ToBE struct { 56 | Dest Register 57 | } 58 | 59 | func (a *End16ToBE) Raw() ([]RawInstruction, error) { 60 | return []RawInstruction{ 61 | {Op: BPF_ALU | BPF_END | BPF_TO_BE, Reg: NewReg(0, a.Dest), Imm: 16}, 62 | }, nil 63 | } 64 | 65 | func (a *End16ToBE) String() string { 66 | return fmt.Sprintf("r%s = be16 r%s", a.Dest, a.Dest) 67 | } 68 | 69 | var _ Instruction = (*End32ToBE)(nil) 70 | 71 | type End32ToBE struct { 72 | Dest Register 73 | } 74 | 75 | func (a *End32ToBE) Raw() ([]RawInstruction, error) { 76 | return []RawInstruction{ 77 | {Op: BPF_ALU | BPF_END | BPF_TO_BE, Reg: NewReg(0, a.Dest), Imm: 32}, 78 | }, nil 79 | } 80 | 81 | func (a *End32ToBE) String() string { 82 | return fmt.Sprintf("r%s = be32 r%s", a.Dest, a.Dest) 83 | } 84 | 85 | var _ Instruction = (*End64ToBE)(nil) 86 | 87 | type End64ToBE struct { 88 | Dest Register 89 | } 90 | 91 | func (a *End64ToBE) Raw() ([]RawInstruction, error) { 92 | return []RawInstruction{ 93 | {Op: BPF_ALU | BPF_END | BPF_TO_BE, Reg: NewReg(0, a.Dest), Imm: 64}, 94 | }, nil 95 | } 96 | 97 | func (a *End64ToBE) String() string { 98 | return fmt.Sprintf("r%s = be64 r%s", a.Dest, a.Dest) 99 | } 100 | -------------------------------------------------------------------------------- /ebpf/exit.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | var _ Instruction = (*Exit)(nil) 4 | 5 | type Exit struct{} 6 | 7 | func (e *Exit) Raw() ([]RawInstruction, error) { 8 | return []RawInstruction{ 9 | {Op: BPF_EXIT | BPF_JMP, Reg: NewReg(0, 0)}, 10 | }, nil 11 | } 12 | 13 | func (e *Exit) String() string { 14 | return "exit" 15 | } 16 | -------------------------------------------------------------------------------- /ebpf/ja.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*Jump)(nil) 7 | _ Jumper = (*Jump)(nil) 8 | ) 9 | 10 | type Jump struct { 11 | Offset int16 12 | } 13 | 14 | func (a *Jump) Raw() ([]RawInstruction, error) { 15 | return []RawInstruction{ 16 | {Op: BPF_JA | BPF_JMP, Off: a.Offset}, 17 | }, nil 18 | } 19 | 20 | func (a *Jump) String() string { 21 | return fmt.Sprintf("goto %+d", a.Offset) 22 | } 23 | 24 | func (a *Jump) SetJumpTarget(relAddr int16) { 25 | a.Offset = relAddr 26 | } 27 | -------------------------------------------------------------------------------- /ebpf/jeq.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpEqual)(nil) 7 | _ Jumper = (*JumpEqual)(nil) 8 | _ Valuer = (*JumpEqual)(nil) 9 | ) 10 | 11 | type JumpEqual struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpEqual) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JEQ | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpEqual) String() string { 24 | return fmt.Sprintf("if r%s == %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpEqual) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpEqual) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpEqual32)(nil) 37 | _ Jumper = (*JumpEqual32)(nil) 38 | _ Valuer = (*JumpEqual32)(nil) 39 | ) 40 | 41 | type JumpEqual32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpEqual32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JEQ | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpEqual32) String() string { 54 | return fmt.Sprintf("if w%s == %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpEqual32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpEqual32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpEqualRegister)(nil) 67 | _ Jumper = (*JumpEqualRegister)(nil) 68 | ) 69 | 70 | type JumpEqualRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpEqualRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JEQ | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpEqualRegister) String() string { 83 | return fmt.Sprintf("if r%s == r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpEqualRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpEqualRegister32)(nil) 92 | _ Jumper = (*JumpEqualRegister32)(nil) 93 | ) 94 | 95 | type JumpEqualRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpEqualRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JEQ | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpEqualRegister32) String() string { 108 | return fmt.Sprintf("if w%s == w%s goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpEqualRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jge.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpGreaterThanEqual)(nil) 7 | _ Jumper = (*JumpGreaterThanEqual)(nil) 8 | _ Valuer = (*JumpGreaterThanEqual)(nil) 9 | ) 10 | 11 | type JumpGreaterThanEqual struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpGreaterThanEqual) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JGE | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpGreaterThanEqual) String() string { 24 | return fmt.Sprintf("if r%s >= %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpGreaterThanEqual) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpGreaterThanEqual) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpGreaterThanEqual32)(nil) 37 | _ Jumper = (*JumpGreaterThanEqual32)(nil) 38 | _ Valuer = (*JumpGreaterThanEqual32)(nil) 39 | ) 40 | 41 | type JumpGreaterThanEqual32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpGreaterThanEqual32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JGE | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpGreaterThanEqual32) String() string { 54 | return fmt.Sprintf("if w%d >= %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpGreaterThanEqual32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpGreaterThanEqual32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpGreaterThanEqualRegister)(nil) 67 | _ Jumper = (*JumpGreaterThanEqualRegister)(nil) 68 | ) 69 | 70 | type JumpGreaterThanEqualRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpGreaterThanEqualRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JGE | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpGreaterThanEqualRegister) String() string { 83 | return fmt.Sprintf("if r%s >= r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpGreaterThanEqualRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpGreaterThanEqualRegister32)(nil) 92 | _ Jumper = (*JumpGreaterThanEqualRegister32)(nil) 93 | ) 94 | 95 | type JumpGreaterThanEqualRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpGreaterThanEqualRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JGE | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpGreaterThanEqualRegister32) String() string { 108 | return fmt.Sprintf("if w%d >= w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpGreaterThanEqualRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jgt.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpGreaterThan)(nil) 7 | _ Jumper = (*JumpGreaterThan)(nil) 8 | _ Valuer = (*JumpGreaterThan)(nil) 9 | ) 10 | 11 | type JumpGreaterThan struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpGreaterThan) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JGT | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpGreaterThan) String() string { 24 | return fmt.Sprintf("if r%s > %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpGreaterThan) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpGreaterThan) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpGreaterThan32)(nil) 37 | _ Jumper = (*JumpGreaterThan32)(nil) 38 | _ Valuer = (*JumpGreaterThan32)(nil) 39 | ) 40 | 41 | type JumpGreaterThan32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpGreaterThan32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JGT | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpGreaterThan32) String() string { 54 | return fmt.Sprintf("if w%d > %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpGreaterThan32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpGreaterThan32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpGreaterThanRegister)(nil) 67 | _ Jumper = (*JumpGreaterThanRegister)(nil) 68 | ) 69 | 70 | type JumpGreaterThanRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpGreaterThanRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JGT | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpGreaterThanRegister) String() string { 83 | return fmt.Sprintf("if r%s > r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpGreaterThanRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpGreaterThanRegister32)(nil) 92 | _ Jumper = (*JumpGreaterThanRegister32)(nil) 93 | ) 94 | 95 | type JumpGreaterThanRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpGreaterThanRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JGT | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpGreaterThanRegister32) String() string { 108 | return fmt.Sprintf("if w%d > w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpGreaterThanRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jle.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpSmallerThanEqual)(nil) 7 | _ Jumper = (*JumpSmallerThanEqual)(nil) 8 | _ Valuer = (*JumpSmallerThanEqual)(nil) 9 | ) 10 | 11 | type JumpSmallerThanEqual struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpSmallerThanEqual) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JLE | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpSmallerThanEqual) String() string { 24 | return fmt.Sprintf("if r%s <= %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpSmallerThanEqual) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpSmallerThanEqual) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpSmallerThanEqual32)(nil) 37 | _ Jumper = (*JumpSmallerThanEqual32)(nil) 38 | _ Valuer = (*JumpSmallerThanEqual32)(nil) 39 | ) 40 | 41 | type JumpSmallerThanEqual32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpSmallerThanEqual32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JLE | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpSmallerThanEqual32) String() string { 54 | return fmt.Sprintf("if w%d <= %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpSmallerThanEqual32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpSmallerThanEqual32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpSmallerThanEqualRegister)(nil) 67 | _ Jumper = (*JumpSmallerThanEqualRegister)(nil) 68 | ) 69 | 70 | type JumpSmallerThanEqualRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpSmallerThanEqualRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JLE | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpSmallerThanEqualRegister) String() string { 83 | return fmt.Sprintf("if r%s <= r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpSmallerThanEqualRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpSmallerThanEqualRegister32)(nil) 92 | _ Jumper = (*JumpSmallerThanEqualRegister32)(nil) 93 | ) 94 | 95 | type JumpSmallerThanEqualRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpSmallerThanEqualRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JLE | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpSmallerThanEqualRegister32) String() string { 108 | return fmt.Sprintf("if w%d <= w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpSmallerThanEqualRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jlt.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpSmallerThan)(nil) 7 | _ Jumper = (*JumpSmallerThan)(nil) 8 | _ Valuer = (*JumpSmallerThan)(nil) 9 | ) 10 | 11 | type JumpSmallerThan struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpSmallerThan) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JLT | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpSmallerThan) String() string { 24 | return fmt.Sprintf("if r%s < %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpSmallerThan) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpSmallerThan) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpSmallerThan32)(nil) 37 | _ Jumper = (*JumpSmallerThan32)(nil) 38 | _ Valuer = (*JumpSmallerThan32)(nil) 39 | ) 40 | 41 | type JumpSmallerThan32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpSmallerThan32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JLT | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpSmallerThan32) String() string { 54 | return fmt.Sprintf("if w%d < %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpSmallerThan32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpSmallerThan32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpSmallerThanRegister)(nil) 67 | _ Jumper = (*JumpSmallerThanRegister)(nil) 68 | ) 69 | 70 | type JumpSmallerThanRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpSmallerThanRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JLT | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpSmallerThanRegister) String() string { 83 | return fmt.Sprintf("if r%s < r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpSmallerThanRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpSmallerThanRegister32)(nil) 92 | _ Jumper = (*JumpSmallerThanRegister32)(nil) 93 | ) 94 | 95 | type JumpSmallerThanRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpSmallerThanRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JLT | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpSmallerThanRegister32) String() string { 108 | return fmt.Sprintf("if w%d < w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpSmallerThanRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jne.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpNotEqual)(nil) 7 | _ Jumper = (*JumpNotEqual)(nil) 8 | _ Valuer = (*JumpNotEqual)(nil) 9 | ) 10 | 11 | type JumpNotEqual struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpNotEqual) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JNE | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpNotEqual) String() string { 24 | return fmt.Sprintf("if r%s != %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpNotEqual) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpNotEqual) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpNotEqual32)(nil) 37 | _ Jumper = (*JumpNotEqual32)(nil) 38 | _ Valuer = (*JumpNotEqual32)(nil) 39 | ) 40 | 41 | type JumpNotEqual32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpNotEqual32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JNE | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpNotEqual32) String() string { 54 | return fmt.Sprintf("if w%s != %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpNotEqual32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpNotEqual32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpNotEqualRegister)(nil) 67 | _ Jumper = (*JumpNotEqualRegister)(nil) 68 | ) 69 | 70 | type JumpNotEqualRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpNotEqualRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JNE | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpNotEqualRegister) String() string { 83 | return fmt.Sprintf("if r%s != r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpNotEqualRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpNotEqualRegister32)(nil) 92 | _ Jumper = (*JumpNotEqualRegister32)(nil) 93 | ) 94 | 95 | type JumpNotEqualRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpNotEqualRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JNE | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpNotEqualRegister32) String() string { 108 | return fmt.Sprintf("if w%s != w%s goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpNotEqualRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jset.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpAnd)(nil) 7 | _ Jumper = (*JumpAnd)(nil) 8 | _ Valuer = (*JumpAnd)(nil) 9 | ) 10 | 11 | type JumpAnd struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpAnd) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JSET | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpAnd) String() string { 24 | return fmt.Sprintf("if r%s & %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpAnd) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpAnd) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpAnd32)(nil) 37 | _ Jumper = (*JumpAnd32)(nil) 38 | _ Valuer = (*JumpAnd32)(nil) 39 | ) 40 | 41 | type JumpAnd32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpAnd32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JSET | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpAnd32) String() string { 54 | return fmt.Sprintf("if w%d & %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpAnd32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpAnd32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpAndRegister)(nil) 67 | _ Jumper = (*JumpAndRegister)(nil) 68 | ) 69 | 70 | type JumpAndRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpAndRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JSET | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpAndRegister) String() string { 83 | return fmt.Sprintf("if r%s & r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpAndRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpAndRegister32)(nil) 92 | _ Jumper = (*JumpAndRegister32)(nil) 93 | ) 94 | 95 | type JumpAndRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpAndRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JSET | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpAndRegister32) String() string { 108 | return fmt.Sprintf("if w%d & w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpAndRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jsgt.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpSignedGreaterThan)(nil) 7 | _ Jumper = (*JumpSignedGreaterThan)(nil) 8 | _ Valuer = (*JumpSignedGreaterThan)(nil) 9 | ) 10 | 11 | type JumpSignedGreaterThan struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpSignedGreaterThan) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JSGT | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpSignedGreaterThan) String() string { 24 | return fmt.Sprintf("if r%s s> %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpSignedGreaterThan) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpSignedGreaterThan) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpSignedGreaterThan32)(nil) 37 | _ Jumper = (*JumpSignedGreaterThan32)(nil) 38 | _ Valuer = (*JumpSignedGreaterThan32)(nil) 39 | ) 40 | 41 | type JumpSignedGreaterThan32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpSignedGreaterThan32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JSGT | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpSignedGreaterThan32) String() string { 54 | return fmt.Sprintf("if w%d s> %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpSignedGreaterThan32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpSignedGreaterThan32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpSignedGreaterThanRegister)(nil) 67 | _ Jumper = (*JumpSignedGreaterThanRegister)(nil) 68 | ) 69 | 70 | type JumpSignedGreaterThanRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpSignedGreaterThanRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JSGT | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpSignedGreaterThanRegister) String() string { 83 | return fmt.Sprintf("if r%s s> r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpSignedGreaterThanRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpSignedGreaterThanRegister32)(nil) 92 | _ Jumper = (*JumpSignedGreaterThanRegister32)(nil) 93 | ) 94 | 95 | type JumpSignedGreaterThanRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpSignedGreaterThanRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JSGT | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpSignedGreaterThanRegister32) String() string { 108 | return fmt.Sprintf("if w%d s> w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpSignedGreaterThanRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/jslt.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var ( 6 | _ Instruction = (*JumpSignedSmallerThan)(nil) 7 | _ Jumper = (*JumpSignedSmallerThan)(nil) 8 | _ Valuer = (*JumpSignedSmallerThan)(nil) 9 | ) 10 | 11 | type JumpSignedSmallerThan struct { 12 | Dest Register 13 | Offset int16 14 | Value int32 15 | } 16 | 17 | func (a *JumpSignedSmallerThan) Raw() ([]RawInstruction, error) { 18 | return []RawInstruction{ 19 | {Op: BPF_JSLT | BPF_K | BPF_JMP, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 20 | }, nil 21 | } 22 | 23 | func (a *JumpSignedSmallerThan) String() string { 24 | return fmt.Sprintf("if r%s s< %d goto %+d", a.Dest, a.Value, a.Offset) 25 | } 26 | 27 | func (a *JumpSignedSmallerThan) SetJumpTarget(relAddr int16) { 28 | a.Offset = relAddr 29 | } 30 | 31 | func (a *JumpSignedSmallerThan) SetValue(value int32) { 32 | a.Value = value 33 | } 34 | 35 | var ( 36 | _ Instruction = (*JumpSignedSmallerThan32)(nil) 37 | _ Jumper = (*JumpSignedSmallerThan32)(nil) 38 | _ Valuer = (*JumpSignedSmallerThan32)(nil) 39 | ) 40 | 41 | type JumpSignedSmallerThan32 struct { 42 | Dest Register 43 | Offset int16 44 | Value int32 45 | } 46 | 47 | func (a *JumpSignedSmallerThan32) Raw() ([]RawInstruction, error) { 48 | return []RawInstruction{ 49 | {Op: BPF_JSLT | BPF_K | BPF_JMP32, Reg: NewReg(0, a.Dest), Off: a.Offset, Imm: a.Value}, 50 | }, nil 51 | } 52 | 53 | func (a *JumpSignedSmallerThan32) String() string { 54 | return fmt.Sprintf("if w%d s< %d goto %+d", a.Dest, a.Value, a.Offset) 55 | } 56 | 57 | func (a *JumpSignedSmallerThan32) SetJumpTarget(relAddr int16) { 58 | a.Offset = relAddr 59 | } 60 | 61 | func (a *JumpSignedSmallerThan32) SetValue(value int32) { 62 | a.Value = value 63 | } 64 | 65 | var ( 66 | _ Instruction = (*JumpSignedSmallerThanRegister)(nil) 67 | _ Jumper = (*JumpSignedSmallerThanRegister)(nil) 68 | ) 69 | 70 | type JumpSignedSmallerThanRegister struct { 71 | Dest Register 72 | Src Register 73 | Offset int16 74 | } 75 | 76 | func (a *JumpSignedSmallerThanRegister) Raw() ([]RawInstruction, error) { 77 | return []RawInstruction{ 78 | {Op: BPF_JSLT | BPF_X | BPF_JMP, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 79 | }, nil 80 | } 81 | 82 | func (a *JumpSignedSmallerThanRegister) String() string { 83 | return fmt.Sprintf("if r%s s< r%s goto %+d", a.Dest, a.Src, a.Offset) 84 | } 85 | 86 | func (a *JumpSignedSmallerThanRegister) SetJumpTarget(relAddr int16) { 87 | a.Offset = relAddr 88 | } 89 | 90 | var ( 91 | _ Instruction = (*JumpSignedSmallerThanRegister32)(nil) 92 | _ Jumper = (*JumpSignedSmallerThanRegister32)(nil) 93 | ) 94 | 95 | type JumpSignedSmallerThanRegister32 struct { 96 | Dest Register 97 | Src Register 98 | Offset int16 99 | } 100 | 101 | func (a *JumpSignedSmallerThanRegister32) Raw() ([]RawInstruction, error) { 102 | return []RawInstruction{ 103 | {Op: BPF_JSLT | BPF_X | BPF_JMP32, Reg: NewReg(a.Src, a.Dest), Off: a.Offset}, 104 | }, nil 105 | } 106 | 107 | func (a *JumpSignedSmallerThanRegister32) String() string { 108 | return fmt.Sprintf("if w%d s< w%d goto %+d", a.Dest, a.Src, a.Offset) 109 | } 110 | 111 | func (a *JumpSignedSmallerThanRegister32) SetJumpTarget(relAddr int16) { 112 | a.Offset = relAddr 113 | } 114 | -------------------------------------------------------------------------------- /ebpf/lsh.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Lsh32)(nil) 6 | 7 | type Lsh32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Lsh32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_LSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Lsh32) String() string { 19 | return fmt.Sprintf("w%s <<= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Lsh64)(nil) 23 | 24 | type Lsh64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Lsh64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_LSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Lsh64) String() string { 36 | return fmt.Sprintf("r%s <<= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Lsh32Register)(nil) 40 | 41 | type Lsh32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Lsh32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_LSH, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Lsh32Register) String() string { 53 | return fmt.Sprintf("w%s <<= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Lsh64Register)(nil) 57 | 58 | type Lsh64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Lsh64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_LSH, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Lsh64Register) String() string { 70 | return fmt.Sprintf("r%s <<= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/mod.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Mod32)(nil) 6 | 7 | type Mod32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Mod32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_MOD, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Mod32) String() string { 19 | return fmt.Sprintf("w%s %%= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Mod64)(nil) 23 | 24 | type Mod64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Mod64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_MOD, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Mod64) String() string { 36 | return fmt.Sprintf("r%s %%= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Mod32Register)(nil) 40 | 41 | type Mod32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Mod32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_MOD, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Mod32Register) String() string { 53 | return fmt.Sprintf("w%s %%= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Mod64Register)(nil) 57 | 58 | type Mod64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Mod64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_MOD, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Mod64Register) String() string { 70 | return fmt.Sprintf("r%s %%= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/mov.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Mov32)(nil) 6 | 7 | type Mov32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Mov32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_MOV, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Mov32) String() string { 19 | return fmt.Sprintf("w%s = %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Mov64)(nil) 23 | 24 | type Mov64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Mov64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_MOV, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Mov64) String() string { 36 | return fmt.Sprintf("r%s = %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Mov32Register)(nil) 40 | 41 | type Mov32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Mov32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_MOV, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Mov32Register) String() string { 53 | return fmt.Sprintf("w%s = w%s", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Mov64Register)(nil) 57 | 58 | type Mov64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Mov64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_MOV, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Mov64Register) String() string { 70 | return fmt.Sprintf("r%s = r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/mul.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Mul32)(nil) 6 | 7 | type Mul32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Mul32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_MUL, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Mul32) String() string { 19 | return fmt.Sprintf("w%s *= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Mul64)(nil) 23 | 24 | type Mul64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Mul64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_MUL, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Mul64) String() string { 36 | return fmt.Sprintf("r%s *= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Mul32Register)(nil) 40 | 41 | type Mul32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Mul32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_MUL, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Mul32Register) String() string { 53 | return fmt.Sprintf("w%s *= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Mul64Register)(nil) 57 | 58 | type Mul64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Mul64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_MUL, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Mul64Register) String() string { 70 | return fmt.Sprintf("r%s *= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/neg.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Neg32)(nil) 6 | 7 | type Neg32 struct { 8 | Dest Register 9 | } 10 | 11 | func (a *Neg32) Raw() ([]RawInstruction, error) { 12 | return []RawInstruction{ 13 | {Op: BPF_ALU | BPF_NEG, Reg: NewReg(0, a.Dest)}, 14 | }, nil 15 | } 16 | 17 | func (a *Neg32) String() string { 18 | return fmt.Sprintf("w%s = -w%s", a.Dest, a.Dest) 19 | } 20 | 21 | var _ Instruction = (*Neg64)(nil) 22 | 23 | type Neg64 struct { 24 | Dest Register 25 | } 26 | 27 | func (a *Neg64) Raw() ([]RawInstruction, error) { 28 | return []RawInstruction{ 29 | {Op: BPF_ALU64 | BPF_NEG, Reg: NewReg(0, a.Dest)}, 30 | }, nil 31 | } 32 | 33 | func (a *Neg64) String() string { 34 | return fmt.Sprintf("r%s = -r%s", a.Dest, a.Dest) 35 | } 36 | -------------------------------------------------------------------------------- /ebpf/or.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Or32)(nil) 6 | 7 | type Or32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Or32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_OR, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Or32) String() string { 19 | return fmt.Sprintf("w%s |= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Or64)(nil) 23 | 24 | type Or64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Or64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_OR, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Or64) String() string { 36 | return fmt.Sprintf("r%s |= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Or32Register)(nil) 40 | 41 | type Or32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Or32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_OR, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Or32Register) String() string { 53 | return fmt.Sprintf("w%s |= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Or64Register)(nil) 57 | 58 | type Or64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Or64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_OR, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Or64Register) String() string { 70 | return fmt.Sprintf("r%s |= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/rsh.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Rsh32)(nil) 6 | 7 | type Rsh32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Rsh32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_RSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Rsh32) String() string { 19 | return fmt.Sprintf("w%s >>= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Rsh64)(nil) 23 | 24 | type Rsh64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Rsh64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_RSH, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Rsh64) String() string { 36 | return fmt.Sprintf("r%s >>= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Rsh32Register)(nil) 40 | 41 | type Rsh32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Rsh32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_RSH, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Rsh32Register) String() string { 53 | return fmt.Sprintf("w%s >>= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Rsh64Register)(nil) 57 | 58 | type Rsh64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Rsh64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_RSH, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Rsh64Register) String() string { 70 | return fmt.Sprintf("r%s >>= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/store.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*StoreMemoryConstant)(nil) 6 | 7 | type StoreMemoryConstant struct { 8 | Dest Register 9 | Size Size 10 | Offset int16 11 | Value int32 12 | } 13 | 14 | func (sm *StoreMemoryConstant) Raw() ([]RawInstruction, error) { 15 | return []RawInstruction{ 16 | { 17 | Op: BPF_ST | uint8(sm.Size) | BPF_MEM, 18 | Reg: NewReg(0, sm.Dest), 19 | Off: sm.Offset, 20 | Imm: sm.Value, 21 | }, 22 | }, nil 23 | } 24 | 25 | func (sm *StoreMemoryConstant) String() string { 26 | sign := "+" 27 | offset := sm.Offset 28 | if offset < 0 { 29 | sign = "-" 30 | offset = -offset 31 | } 32 | return fmt.Sprintf("*(%s *)(r%s %s %d) = %d", sm.Size, sm.Dest, sign, offset, sm.Value) 33 | } 34 | 35 | var _ Instruction = (*StoreMemoryRegister)(nil) 36 | 37 | type StoreMemoryRegister struct { 38 | Src Register 39 | Dest Register 40 | Offset int16 41 | Size Size 42 | } 43 | 44 | func (sm *StoreMemoryRegister) Raw() ([]RawInstruction, error) { 45 | return []RawInstruction{ 46 | { 47 | Op: BPF_STX | uint8(sm.Size) | BPF_MEM, 48 | Reg: NewReg(sm.Src, sm.Dest), 49 | Off: sm.Offset, 50 | }, 51 | }, nil 52 | } 53 | 54 | func (sm *StoreMemoryRegister) String() string { 55 | sign := "+" 56 | offset := sm.Offset 57 | if offset < 0 { 58 | sign = "-" 59 | offset = -offset 60 | } 61 | return fmt.Sprintf("*(%s *)(r%s %s %d) = r%s", sm.Size, sm.Dest, sign, offset, sm.Src) 62 | } 63 | -------------------------------------------------------------------------------- /ebpf/sub.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Sub32)(nil) 6 | 7 | type Sub32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Sub32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_SUB, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Sub32) String() string { 19 | return fmt.Sprintf("w%s -= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Sub64)(nil) 23 | 24 | type Sub64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Sub64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_SUB, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Sub64) String() string { 36 | return fmt.Sprintf("r%s -= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Sub32Register)(nil) 40 | 41 | type Sub32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Sub32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_SUB, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Sub32Register) String() string { 53 | return fmt.Sprintf("w%s -= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Sub64Register)(nil) 57 | 58 | type Sub64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Sub64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_SUB, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Sub64Register) String() string { 70 | return fmt.Sprintf("r%s -= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /ebpf/xor.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import "fmt" 4 | 5 | var _ Instruction = (*Xor32)(nil) 6 | 7 | type Xor32 struct { 8 | Dest Register 9 | Value int32 10 | } 11 | 12 | func (a *Xor32) Raw() ([]RawInstruction, error) { 13 | return []RawInstruction{ 14 | {Op: BPF_ALU | BPF_K | BPF_XOR, Reg: NewReg(0, a.Dest), Imm: a.Value}, 15 | }, nil 16 | } 17 | 18 | func (a *Xor32) String() string { 19 | return fmt.Sprintf("w%s ^= %d", a.Dest, a.Value) 20 | } 21 | 22 | var _ Instruction = (*Xor64)(nil) 23 | 24 | type Xor64 struct { 25 | Dest Register 26 | Value int32 27 | } 28 | 29 | func (a *Xor64) Raw() ([]RawInstruction, error) { 30 | return []RawInstruction{ 31 | {Op: BPF_ALU64 | BPF_K | BPF_XOR, Reg: NewReg(0, a.Dest), Imm: a.Value}, 32 | }, nil 33 | } 34 | 35 | func (a *Xor64) String() string { 36 | return fmt.Sprintf("r%s ^= %d", a.Dest, a.Value) 37 | } 38 | 39 | var _ Instruction = (*Xor32Register)(nil) 40 | 41 | type Xor32Register struct { 42 | Dest Register 43 | Src Register 44 | } 45 | 46 | func (a *Xor32Register) Raw() ([]RawInstruction, error) { 47 | return []RawInstruction{ 48 | {Op: BPF_ALU | BPF_X | BPF_XOR, Reg: NewReg(a.Src, a.Dest)}, 49 | }, nil 50 | } 51 | 52 | func (a *Xor32Register) String() string { 53 | return fmt.Sprintf("w%s ^= w%d", a.Dest, a.Src) 54 | } 55 | 56 | var _ Instruction = (*Xor64Register)(nil) 57 | 58 | type Xor64Register struct { 59 | Dest Register 60 | Src Register 61 | } 62 | 63 | func (a *Xor64Register) Raw() ([]RawInstruction, error) { 64 | return []RawInstruction{ 65 | {Op: BPF_ALU64 | BPF_X | BPF_XOR, Reg: NewReg(a.Src, a.Dest)}, 66 | }, nil 67 | } 68 | 69 | func (a *Xor64Register) String() string { 70 | return fmt.Sprintf("r%s ^= r%s", a.Dest, a.Src) 71 | } 72 | -------------------------------------------------------------------------------- /emulator/doc.go: -------------------------------------------------------------------------------- 1 | // package emulator contains a userspace emulator/runtime for eBPF. 2 | // 3 | // The primary use cases I can currently think of are: 4 | // * Debugging, we can't debug eBPF programs when they run in the kernel, it would be nice if we have the option 5 | // to start a debugger session for an eBPF program just like we do with dlv or gdb for userspace applications. 6 | // * Dynamic program extension, ultimately eBPF was made as a sort of plugin language for the kernel, but it doesn't 7 | // have to be limited to that. We could create a plugin system for go programs based on eBPF code, much like how 8 | // some people use lua or javascript. 9 | // 10 | // eBPF was originally developed for use in Linux, but it doesn't have to be limited to that. In fact work is already 11 | // underway to use eBPF in BSD's and Windows. There is noting inherently "Linux" about eBPF itself, all of the "Host" 12 | // specific features are implemented via map types and helper functions. Therefor this emulator is architected in such 13 | // a way that it can be used for both Linux-flavored eBPF programs as well as others inclusing custom map and helper 14 | // function types dedicated for other applications. 15 | package emulator 16 | -------------------------------------------------------------------------------- /emulator/inst_add.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Add32)(nil) 10 | 11 | type Add32 struct { 12 | ebpf.Add32 13 | } 14 | 15 | func (i *Add32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Add32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) + i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Add64)(nil) 35 | 36 | type Add64 struct { 37 | ebpf.Add64 38 | } 39 | 40 | func (i *Add64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Add64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv + int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Add32Register)(nil) 60 | 61 | type Add32Register struct { 62 | ebpf.Add32Register 63 | } 64 | 65 | func (i *Add32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Add32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, sr, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | // Edge case: if we add a pointer to a value, we want to convert the destination type into a pointer as well 82 | if _, ok := sr.(PointerValue); ok { 83 | scp, err := vm.Registers.Copy(i.Src) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | err = scp.Assign(int64(int32(dv) + int32(sv))) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | err = vm.Registers.Assign(i.Dest, scp) 94 | if err != nil { 95 | return err 96 | } 97 | return nil 98 | } 99 | 100 | err = dr.Assign(int64(int32(dv) + int32(sv))) 101 | if err != nil { 102 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 103 | } 104 | 105 | return nil 106 | } 107 | 108 | var _ Instruction = (*Add64Register)(nil) 109 | 110 | type Add64Register struct { 111 | ebpf.Add64Register 112 | } 113 | 114 | func (i *Add64Register) Clone() Instruction { 115 | c := *i 116 | return &c 117 | } 118 | 119 | func (i *Add64Register) Execute(vm *VM) error { 120 | dv, dr, err := readReg(vm, i.Dest) 121 | if err != nil { 122 | return err 123 | } 124 | 125 | sv, sr, err := readReg(vm, i.Src) 126 | if err != nil { 127 | return err 128 | } 129 | 130 | // Edge case: if we add a pointer to a value, we want to convert the destination type into a pointer as well 131 | if _, ok := sr.(PointerValue); ok { 132 | scp, err := vm.Registers.Copy(i.Src) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | err = scp.Assign(dv + sv) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | err = vm.Registers.Assign(i.Dest, scp) 143 | if err != nil { 144 | return err 145 | } 146 | return nil 147 | } 148 | 149 | err = dr.Assign(dv + sv) 150 | if err != nil { 151 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 152 | } 153 | 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /emulator/inst_and.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*And32)(nil) 10 | 11 | type And32 struct { 12 | ebpf.And32 13 | } 14 | 15 | func (i *And32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *And32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) & i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*And64)(nil) 35 | 36 | type And64 struct { 37 | ebpf.And64 38 | } 39 | 40 | func (i *And64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *And64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv & int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*And32Register)(nil) 60 | 61 | type And32Register struct { 62 | ebpf.And32Register 63 | } 64 | 65 | func (i *And32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *And32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) & int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*And64Register)(nil) 90 | 91 | type And64Register struct { 92 | ebpf.And64Register 93 | } 94 | 95 | func (i *And64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *And64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv & sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_arsh.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*ARSH32)(nil) 10 | 11 | type ARSH32 struct { 12 | ebpf.ARSH32 13 | } 14 | 15 | func (i *ARSH32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *ARSH32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) >> i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*ARSH64)(nil) 35 | 36 | type ARSH64 struct { 37 | ebpf.ARSH64 38 | } 39 | 40 | func (i *ARSH64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *ARSH64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv >> int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*ARSH32Register)(nil) 60 | 61 | type ARSH32Register struct { 62 | ebpf.ARSH32Register 63 | } 64 | 65 | func (i *ARSH32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *ARSH32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) >> int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*ARSH64Register)(nil) 90 | 91 | type ARSH64Register struct { 92 | ebpf.ARSH64Register 93 | } 94 | 95 | func (i *ARSH64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *ARSH64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv >> sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_atomic.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*AtomicAdd)(nil) 10 | 11 | type AtomicAdd struct { 12 | ebpf.AtomicAdd 13 | } 14 | 15 | func (i *AtomicAdd) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *AtomicAdd) Execute(vm *VM) error { 21 | // TODO make actually atomic using a mutex or similar when adding multi threading support. 22 | dr, err := vm.Registers.Copy(i.Dest) 23 | if err != nil { 24 | return fmt.Errorf("copy %s: %w", i.Dest, err) 25 | } 26 | 27 | var off int64 28 | var memory Memory 29 | switch dmp := dr.(type) { 30 | case *MemoryPtr: 31 | // Memory pointers point to the start of a memory block 32 | off = dmp.Offset + int64(i.Offset) 33 | memory = dmp.Memory 34 | 35 | case *FramePointer: 36 | // Frame pointers point to the end of a stack frame 37 | off = int64(dmp.Memory.Size()) + dmp.Offset + int64(i.Offset) 38 | memory = dmp.Memory 39 | 40 | default: 41 | return fmt.Errorf("can't store to a non-pointer register value") 42 | } 43 | 44 | dv, err := memory.Read(int(off), i.Size) 45 | if err != nil { 46 | return fmt.Errorf("memory read: %w", err) 47 | } 48 | 49 | sr, err := vm.Registers.Get(i.Src) 50 | if err != nil { 51 | return fmt.Errorf("get src: %w", err) 52 | } 53 | 54 | err = dv.Assign(dv.Value() + sr.Value()) 55 | if err != nil { 56 | return fmt.Errorf("assign value: %w", err) 57 | } 58 | 59 | err = memory.Write(int(off), dv, i.Size) 60 | if err != nil { 61 | return fmt.Errorf("write dst+src: %w", err) 62 | } 63 | 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /emulator/inst_call_bpf.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*CallBPF)(nil) 8 | 9 | type CallBPF struct { 10 | ebpf.CallBPF 11 | } 12 | 13 | func (i *CallBPF) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *CallBPF) Execute(vm *VM) error { 19 | // Preserve current registers 20 | vm.PreservedRegisters = append(vm.PreservedRegisters, vm.Registers.Clone()) 21 | 22 | // Change R10 to the next stack frame 23 | vm.Registers.R10 = FramePointer{ 24 | Memory: &vm.StackFrames[vm.Registers.R10.Index+1], 25 | Index: vm.Registers.R10.Index + 1, 26 | Offset: 0, 27 | Readonly: true, 28 | } 29 | 30 | // Wipe the stack frame in case there was any memory from earlier use 31 | frame, ok := vm.Registers.R10.Memory.(*ValueMemory) 32 | if !ok { 33 | panic("r10 != *ValueMemory") 34 | } 35 | 36 | for i := range frame.Mapping { 37 | frame.Mapping[i] = nil 38 | } 39 | 40 | // Change the program counter to the function. 41 | vm.Registers.PC += int(i.Offset) 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /emulator/inst_call_helper.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*CallHelper)(nil) 10 | 11 | type CallHelper struct { 12 | ebpf.CallHelper 13 | } 14 | 15 | func (i *CallHelper) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *CallHelper) Execute(vm *VM) error { 21 | if int(i.Function) >= len(vm.HelperFunctions) { 22 | return fmt.Errorf("VM has no helper function for ID '%d'", i.Function) 23 | } 24 | 25 | f := vm.HelperFunctions[i.Function] 26 | if f == nil { 27 | return fmt.Errorf("VM has no helper function for ID '%d'", i.Function) 28 | } 29 | 30 | err := f(vm) 31 | if err != nil { 32 | return fmt.Errorf("helper function paniced: %w", err) 33 | } 34 | 35 | return nil 36 | } 37 | 38 | var _ Instruction = (*CallHelperIndirect)(nil) 39 | 40 | type CallHelperIndirect struct { 41 | ebpf.CallHelperIndirect 42 | } 43 | 44 | func (i *CallHelperIndirect) Clone() Instruction { 45 | c := *i 46 | return &c 47 | } 48 | 49 | func (i *CallHelperIndirect) Execute(vm *VM) error { 50 | fReg, err := vm.Registers.Get(i.Register) 51 | if err != nil { 52 | return fmt.Errorf("get reg: %w", err) 53 | } 54 | fVal := fReg.Value() 55 | 56 | if int(fVal) >= len(vm.HelperFunctions) { 57 | return fmt.Errorf("VM has no helper function for ID '%d'", fVal) 58 | } 59 | 60 | f := vm.HelperFunctions[fVal] 61 | if f == nil { 62 | return fmt.Errorf("VM has no helper function for ID '%d'", fVal) 63 | } 64 | 65 | err = f(vm) 66 | if err != nil { 67 | return fmt.Errorf("helper function '%d' paniced: %w", fVal, err) 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /emulator/inst_div.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/dylandreimerink/gobpfld/ebpf" 8 | ) 9 | 10 | var errDivByZero = errors.New("divide by zero") 11 | 12 | var _ Instruction = (*Div32)(nil) 13 | 14 | type Div32 struct { 15 | ebpf.Div32 16 | } 17 | 18 | func (i *Div32) Clone() Instruction { 19 | c := *i 20 | return &c 21 | } 22 | 23 | func (i *Div32) Execute(vm *VM) error { 24 | rv, r, err := readReg(vm, i.Dest) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if i.Value == 0 { 30 | return errDivByZero 31 | } 32 | 33 | err = r.Assign(int64(int32(rv) / i.Value)) 34 | if err != nil { 35 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 36 | } 37 | 38 | return nil 39 | } 40 | 41 | var _ Instruction = (*Div64)(nil) 42 | 43 | type Div64 struct { 44 | ebpf.Div64 45 | } 46 | 47 | func (i *Div64) Clone() Instruction { 48 | c := *i 49 | return &c 50 | } 51 | 52 | func (i *Div64) Execute(vm *VM) error { 53 | rv, r, err := readReg(vm, i.Dest) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | if i.Value == 0 { 59 | return errDivByZero 60 | } 61 | 62 | err = r.Assign(rv / int64(i.Value)) 63 | if err != nil { 64 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 65 | } 66 | 67 | return nil 68 | } 69 | 70 | var _ Instruction = (*Div32Register)(nil) 71 | 72 | type Div32Register struct { 73 | ebpf.Div32Register 74 | } 75 | 76 | func (i *Div32Register) Clone() Instruction { 77 | c := *i 78 | return &c 79 | } 80 | 81 | func (i *Div32Register) Execute(vm *VM) error { 82 | dv, dr, err := readReg(vm, i.Dest) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | sv, _, err := readReg(vm, i.Src) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | if sv == 0 { 93 | return errDivByZero 94 | } 95 | 96 | err = dr.Assign(int64(int32(dv) / int32(sv))) 97 | if err != nil { 98 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 99 | } 100 | 101 | return nil 102 | } 103 | 104 | var _ Instruction = (*Div64Register)(nil) 105 | 106 | type Div64Register struct { 107 | ebpf.Div64Register 108 | } 109 | 110 | func (i *Div64Register) Clone() Instruction { 111 | c := *i 112 | return &c 113 | } 114 | 115 | func (i *Div64Register) Execute(vm *VM) error { 116 | dv, dr, err := readReg(vm, i.Dest) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | sv, _, err := readReg(vm, i.Src) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | if sv == 0 { 127 | return errDivByZero 128 | } 129 | 130 | err = dr.Assign(dv / sv) 131 | if err != nil { 132 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /emulator/inst_exit.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Exit)(nil) 10 | 11 | type Exit struct { 12 | ebpf.Exit 13 | } 14 | 15 | var errExit = errors.New("function/program exit") 16 | 17 | func (i *Exit) Clone() Instruction { 18 | c := *i 19 | return &c 20 | } 21 | 22 | func (i *Exit) Execute(vm *VM) error { 23 | // If the call stack is empty, we exit the program 24 | if len(vm.PreservedRegisters) == 0 { 25 | return errExit 26 | } 27 | 28 | // If there are values on the call stack, this is a return statement 29 | preserved := vm.PreservedRegisters[len(vm.PreservedRegisters)-1] 30 | vm.PreservedRegisters = vm.PreservedRegisters[:len(vm.PreservedRegisters)-1] 31 | 32 | // Restore preserved Program counter and callee saved registers 33 | vm.Registers.PC = preserved.PC 34 | vm.Registers.R6 = preserved.R6 35 | vm.Registers.R7 = preserved.R7 36 | vm.Registers.R8 = preserved.R8 37 | vm.Registers.R9 = preserved.R9 38 | 39 | // Restore the previous stack frame 40 | vm.Registers.R10 = FramePointer{ 41 | Memory: &vm.StackFrames[vm.Registers.R10.Index-1], 42 | Index: vm.Registers.R10.Index - 1, 43 | Offset: 0, 44 | Readonly: true, 45 | } 46 | 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /emulator/inst_ja.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*Jump)(nil) 8 | 9 | type Jump struct { 10 | ebpf.Jump 11 | } 12 | 13 | func (i *Jump) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *Jump) Execute(vm *VM) error { 19 | vm.Registers.PC += int(i.Offset) 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /emulator/inst_jeq.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpEqual32)(nil) 8 | 9 | type JumpEqual32 struct { 10 | ebpf.JumpEqual32 11 | } 12 | 13 | func (i *JumpEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv) == i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpEqual)(nil) 32 | 33 | type JumpEqual struct { 34 | ebpf.JumpEqual 35 | } 36 | 37 | func (i *JumpEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv == int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpEqualRegister32)(nil) 56 | 57 | type JumpEqualRegister32 struct { 58 | ebpf.JumpEqualRegister32 59 | } 60 | 61 | func (i *JumpEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv) == int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpEqualRegister)(nil) 85 | 86 | type JumpEqualRegister struct { 87 | ebpf.JumpEqualRegister 88 | } 89 | 90 | func (i *JumpEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv == sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jge.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpGreaterThanEqual32)(nil) 8 | 9 | type JumpGreaterThanEqual32 struct { 10 | ebpf.JumpGreaterThanEqual32 11 | } 12 | 13 | func (i *JumpGreaterThanEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpGreaterThanEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && uint32(dv) >= uint32(i.Value) { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpGreaterThanEqual)(nil) 32 | 33 | type JumpGreaterThanEqual struct { 34 | ebpf.JumpGreaterThanEqual 35 | } 36 | 37 | func (i *JumpGreaterThanEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpGreaterThanEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && uint64(dv) >= uint64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpGreaterThanEqualRegister32)(nil) 56 | 57 | type JumpGreaterThanEqualRegister32 struct { 58 | ebpf.JumpGreaterThanEqualRegister32 59 | } 60 | 61 | func (i *JumpGreaterThanEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpGreaterThanEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && uint32(dv) >= uint32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpGreaterThanEqualRegister)(nil) 85 | 86 | type JumpGreaterThanEqualRegister struct { 87 | ebpf.JumpGreaterThanEqualRegister 88 | } 89 | 90 | func (i *JumpGreaterThanEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpGreaterThanEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && uint64(dv) >= uint64(sv) { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jgt.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpGreaterThan32)(nil) 8 | 9 | type JumpGreaterThan32 struct { 10 | ebpf.JumpGreaterThan32 11 | } 12 | 13 | func (i *JumpGreaterThan32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpGreaterThan32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && uint32(dv) > uint32(i.Value) { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpGreaterThan)(nil) 32 | 33 | type JumpGreaterThan struct { 34 | ebpf.JumpGreaterThan 35 | } 36 | 37 | func (i *JumpGreaterThan) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpGreaterThan) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && uint64(dv) > uint64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpGreaterThanRegister32)(nil) 56 | 57 | type JumpGreaterThanRegister32 struct { 58 | ebpf.JumpGreaterThanRegister32 59 | } 60 | 61 | func (i *JumpGreaterThanRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpGreaterThanRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && uint32(dv) > uint32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpGreaterThanRegister)(nil) 85 | 86 | type JumpGreaterThanRegister struct { 87 | ebpf.JumpGreaterThanRegister 88 | } 89 | 90 | func (i *JumpGreaterThanRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpGreaterThanRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && uint64(dv) > uint64(sv) { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jle.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSmallerThanEqual32)(nil) 8 | 9 | type JumpSmallerThanEqual32 struct { 10 | ebpf.JumpSmallerThanEqual32 11 | } 12 | 13 | func (i *JumpSmallerThanEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSmallerThanEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && uint32(dv) <= uint32(i.Value) { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSmallerThanEqual)(nil) 32 | 33 | type JumpSmallerThanEqual struct { 34 | ebpf.JumpSmallerThanEqual 35 | } 36 | 37 | func (i *JumpSmallerThanEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSmallerThanEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && uint64(dv) <= uint64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSmallerThanEqualRegister32)(nil) 56 | 57 | type JumpSmallerThanEqualRegister32 struct { 58 | ebpf.JumpSmallerThanEqualRegister32 59 | } 60 | 61 | func (i *JumpSmallerThanEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSmallerThanEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && uint32(dv) <= uint32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSmallerThanEqualRegister)(nil) 85 | 86 | type JumpSmallerThanEqualRegister struct { 87 | ebpf.JumpSmallerThanEqualRegister 88 | } 89 | 90 | func (i *JumpSmallerThanEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSmallerThanEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && uint64(dv) <= uint64(sv) { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jlt.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSmallerThan32)(nil) 8 | 9 | type JumpSmallerThan32 struct { 10 | ebpf.JumpSmallerThan32 11 | } 12 | 13 | func (i *JumpSmallerThan32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSmallerThan32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && uint32(dv) < uint32(i.Value) { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSmallerThan)(nil) 32 | 33 | type JumpSmallerThan struct { 34 | ebpf.JumpSmallerThan 35 | } 36 | 37 | func (i *JumpSmallerThan) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSmallerThan) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && uint64(dv) < uint64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSmallerThanRegister32)(nil) 56 | 57 | type JumpSmallerThanRegister32 struct { 58 | ebpf.JumpSmallerThanRegister32 59 | } 60 | 61 | func (i *JumpSmallerThanRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSmallerThanRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && uint32(dv) < uint32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSmallerThanRegister)(nil) 85 | 86 | type JumpSmallerThanRegister struct { 87 | ebpf.JumpSmallerThanRegister 88 | } 89 | 90 | func (i *JumpSmallerThanRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSmallerThanRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && uint64(dv) < uint64(sv) { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jne.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpNotEqual32)(nil) 8 | 9 | type JumpNotEqual32 struct { 10 | ebpf.JumpNotEqual32 11 | } 12 | 13 | func (i *JumpNotEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpNotEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if !isIMM(dr) || int32(dv) != i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpNotEqual)(nil) 32 | 33 | type JumpNotEqual struct { 34 | ebpf.JumpNotEqual 35 | } 36 | 37 | func (i *JumpNotEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpNotEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if !isIMM(dr) || dv != int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpNotEqualRegister32)(nil) 56 | 57 | type JumpNotEqualRegister32 struct { 58 | ebpf.JumpNotEqualRegister32 59 | } 60 | 61 | func (i *JumpNotEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpNotEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if !sameRVType(dr, sr) || int32(dv) != int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpNotEqualRegister)(nil) 85 | 86 | type JumpNotEqualRegister struct { 87 | ebpf.JumpNotEqualRegister 88 | } 89 | 90 | func (i *JumpNotEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpNotEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if !sameRVType(dr, sr) || dv != sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jset.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpAnd32)(nil) 8 | 9 | type JumpAnd32 struct { 10 | ebpf.JumpAnd32 11 | } 12 | 13 | func (i *JumpAnd32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpAnd32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv)&i.Value == 0 { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpAnd)(nil) 32 | 33 | type JumpAnd struct { 34 | ebpf.JumpAnd 35 | } 36 | 37 | func (i *JumpAnd) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpAnd) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv&int64(i.Value) == 0 { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpAndRegister32)(nil) 56 | 57 | type JumpAndRegister32 struct { 58 | ebpf.JumpAndRegister32 59 | } 60 | 61 | func (i *JumpAndRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpAndRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv)&int32(sv) == 0 { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpAndRegister)(nil) 85 | 86 | type JumpAndRegister struct { 87 | ebpf.JumpAndRegister 88 | } 89 | 90 | func (i *JumpAndRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpAndRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv&sv == 0 { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jsge.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSignedGreaterThanOrEqual32)(nil) 8 | 9 | type JumpSignedGreaterThanOrEqual32 struct { 10 | ebpf.JumpSignedGreaterThanOrEqual32 11 | } 12 | 13 | func (i *JumpSignedGreaterThanOrEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSignedGreaterThanOrEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv) >= i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSignedGreaterThanOrEqual)(nil) 32 | 33 | type JumpSignedGreaterThanOrEqual struct { 34 | ebpf.JumpSignedGreaterThanOrEqual 35 | } 36 | 37 | func (i *JumpSignedGreaterThanOrEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSignedGreaterThanOrEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv >= int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSignedGreaterThanOrEqualRegister32)(nil) 56 | 57 | type JumpSignedGreaterThanOrEqualRegister32 struct { 58 | ebpf.JumpSignedGreaterThanOrEqualRegister32 59 | } 60 | 61 | func (i *JumpSignedGreaterThanOrEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSignedGreaterThanOrEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv) >= int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSignedGreaterThanOrEqualRegister)(nil) 85 | 86 | type JumpSignedGreaterThanOrEqualRegister struct { 87 | ebpf.JumpSignedGreaterThanOrEqualRegister 88 | } 89 | 90 | func (i *JumpSignedGreaterThanOrEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSignedGreaterThanOrEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv >= sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jsgt.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSignedGreaterThan32)(nil) 8 | 9 | type JumpSignedGreaterThan32 struct { 10 | ebpf.JumpSignedGreaterThan32 11 | } 12 | 13 | func (i *JumpSignedGreaterThan32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSignedGreaterThan32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv) > i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSignedGreaterThan)(nil) 32 | 33 | type JumpSignedGreaterThan struct { 34 | ebpf.JumpSignedGreaterThan 35 | } 36 | 37 | func (i *JumpSignedGreaterThan) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSignedGreaterThan) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv > int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSignedGreaterThanRegister32)(nil) 56 | 57 | type JumpSignedGreaterThanRegister32 struct { 58 | ebpf.JumpSignedGreaterThanRegister32 59 | } 60 | 61 | func (i *JumpSignedGreaterThanRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSignedGreaterThanRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv) > int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSignedGreaterThanRegister)(nil) 85 | 86 | type JumpSignedGreaterThanRegister struct { 87 | ebpf.JumpSignedGreaterThanRegister 88 | } 89 | 90 | func (i *JumpSignedGreaterThanRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSignedGreaterThanRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv > sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jsle.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSignedSmallerThanOrEqual32)(nil) 8 | 9 | type JumpSignedSmallerThanOrEqual32 struct { 10 | ebpf.JumpSignedSmallerThanOrEqual32 11 | } 12 | 13 | func (i *JumpSignedSmallerThanOrEqual32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSignedSmallerThanOrEqual32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv) <= i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSignedSmallerThanOrEqual)(nil) 32 | 33 | type JumpSignedSmallerThanOrEqual struct { 34 | ebpf.JumpSignedSmallerThanOrEqual 35 | } 36 | 37 | func (i *JumpSignedSmallerThanOrEqual) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSignedSmallerThanOrEqual) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv <= int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSignedSmallerThanOrEqualRegister32)(nil) 56 | 57 | type JumpSignedSmallerThanOrEqualRegister32 struct { 58 | ebpf.JumpSignedSmallerThanOrEqualRegister32 59 | } 60 | 61 | func (i *JumpSignedSmallerThanOrEqualRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSignedSmallerThanOrEqualRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv) <= int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSignedSmallerThanOrEqualRegister)(nil) 85 | 86 | type JumpSignedSmallerThanOrEqualRegister struct { 87 | ebpf.JumpSignedSmallerThanOrEqualRegister 88 | } 89 | 90 | func (i *JumpSignedSmallerThanOrEqualRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSignedSmallerThanOrEqualRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv <= sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_jslt.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*JumpSignedSmallerThan32)(nil) 8 | 9 | type JumpSignedSmallerThan32 struct { 10 | ebpf.JumpSignedSmallerThan32 11 | } 12 | 13 | func (i *JumpSignedSmallerThan32) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *JumpSignedSmallerThan32) Execute(vm *VM) error { 19 | dv, dr, err := readReg(vm, i.Dest) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | if isIMM(dr) && int32(dv) <= i.Value { 25 | vm.Registers.PC += int(i.Offset) 26 | } 27 | 28 | return nil 29 | } 30 | 31 | var _ Instruction = (*JumpSignedSmallerThan)(nil) 32 | 33 | type JumpSignedSmallerThan struct { 34 | ebpf.JumpSignedSmallerThan 35 | } 36 | 37 | func (i *JumpSignedSmallerThan) Clone() Instruction { 38 | c := *i 39 | return &c 40 | } 41 | 42 | func (i *JumpSignedSmallerThan) Execute(vm *VM) error { 43 | dv, dr, err := readReg(vm, i.Dest) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if isIMM(dr) && dv <= int64(i.Value) { 49 | vm.Registers.PC += int(i.Offset) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | var _ Instruction = (*JumpSignedSmallerThanRegister32)(nil) 56 | 57 | type JumpSignedSmallerThanRegister32 struct { 58 | ebpf.JumpSignedSmallerThanRegister32 59 | } 60 | 61 | func (i *JumpSignedSmallerThanRegister32) Clone() Instruction { 62 | c := *i 63 | return &c 64 | } 65 | 66 | func (i *JumpSignedSmallerThanRegister32) Execute(vm *VM) error { 67 | dv, dr, err := readReg(vm, i.Dest) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | sv, sr, err := readReg(vm, i.Src) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if sameRVType(dr, sr) && int32(dv) <= int32(sv) { 78 | vm.Registers.PC += int(i.Offset) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | var _ Instruction = (*JumpSignedSmallerThanRegister)(nil) 85 | 86 | type JumpSignedSmallerThanRegister struct { 87 | ebpf.JumpSignedSmallerThanRegister 88 | } 89 | 90 | func (i *JumpSignedSmallerThanRegister) Clone() Instruction { 91 | c := *i 92 | return &c 93 | } 94 | 95 | func (i *JumpSignedSmallerThanRegister) Execute(vm *VM) error { 96 | dv, dr, err := readReg(vm, i.Dest) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | sv, sr, err := readReg(vm, i.Src) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if sameRVType(dr, sr) && dv <= sv { 107 | vm.Registers.PC += int(i.Offset) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /emulator/inst_lsh.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Lsh32)(nil) 10 | 11 | type Lsh32 struct { 12 | ebpf.Lsh32 13 | } 14 | 15 | func (i *Lsh32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Lsh32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(uint32(rv) << i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Lsh64)(nil) 35 | 36 | type Lsh64 struct { 37 | ebpf.Lsh64 38 | } 39 | 40 | func (i *Lsh64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Lsh64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(int64(uint64(rv) << i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Lsh32Register)(nil) 60 | 61 | type Lsh32Register struct { 62 | ebpf.Lsh32Register 63 | } 64 | 65 | func (i *Lsh32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Lsh32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(uint32(dv) << int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Lsh64Register)(nil) 90 | 91 | type Lsh64Register struct { 92 | ebpf.Lsh64Register 93 | } 94 | 95 | func (i *Lsh64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Lsh64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(int64(uint64(dv) << sv)) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_mod.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/dylandreimerink/gobpfld/ebpf" 8 | ) 9 | 10 | var errModByZero = errors.New("divide by zero") 11 | 12 | var _ Instruction = (*Mod32)(nil) 13 | 14 | type Mod32 struct { 15 | ebpf.Mod32 16 | } 17 | 18 | func (i *Mod32) Clone() Instruction { 19 | c := *i 20 | return &c 21 | } 22 | 23 | func (i *Mod32) Execute(vm *VM) error { 24 | rv, r, err := readReg(vm, i.Dest) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if i.Value == 0 { 30 | return errModByZero 31 | } 32 | 33 | err = r.Assign(int64(int32(rv) % i.Value)) 34 | if err != nil { 35 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 36 | } 37 | 38 | return nil 39 | } 40 | 41 | var _ Instruction = (*Mod64)(nil) 42 | 43 | type Mod64 struct { 44 | ebpf.Mod64 45 | } 46 | 47 | func (i *Mod64) Clone() Instruction { 48 | c := *i 49 | return &c 50 | } 51 | 52 | func (i *Mod64) Execute(vm *VM) error { 53 | rv, r, err := readReg(vm, i.Dest) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | if i.Value == 0 { 59 | return errModByZero 60 | } 61 | 62 | err = r.Assign(rv % int64(i.Value)) 63 | if err != nil { 64 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 65 | } 66 | 67 | return nil 68 | } 69 | 70 | var _ Instruction = (*Mod32Register)(nil) 71 | 72 | type Mod32Register struct { 73 | ebpf.Mod32Register 74 | } 75 | 76 | func (i *Mod32Register) Clone() Instruction { 77 | c := *i 78 | return &c 79 | } 80 | 81 | func (i *Mod32Register) Execute(vm *VM) error { 82 | dv, dr, err := readReg(vm, i.Dest) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | sv, _, err := readReg(vm, i.Src) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | if sv == 0 { 93 | return errModByZero 94 | } 95 | 96 | err = dr.Assign(int64(int32(dv) % int32(sv))) 97 | if err != nil { 98 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 99 | } 100 | 101 | return nil 102 | } 103 | 104 | var _ Instruction = (*Mod64Register)(nil) 105 | 106 | type Mod64Register struct { 107 | ebpf.Mod64Register 108 | } 109 | 110 | func (i *Mod64Register) Clone() Instruction { 111 | c := *i 112 | return &c 113 | } 114 | 115 | func (i *Mod64Register) Execute(vm *VM) error { 116 | dv, dr, err := readReg(vm, i.Dest) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | sv, _, err := readReg(vm, i.Src) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | if sv == 0 { 127 | return errModByZero 128 | } 129 | 130 | err = dr.Assign(dv % sv) 131 | if err != nil { 132 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /emulator/inst_mov.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Mov32)(nil) 10 | 11 | type Mov32 struct { 12 | ebpf.Mov32 13 | } 14 | 15 | func (i *Mov32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Mov32) Execute(vm *VM) error { 21 | // TODO is this correct, or should we preseve the upper 32 bits? 22 | err := vm.Registers.Assign(i.Dest, newIMM(int64(i.Value))) 23 | if err != nil { 24 | return fmt.Errorf("assign %s: %w", i.Dest.String(), err) 25 | } 26 | 27 | return nil 28 | } 29 | 30 | var _ Instruction = (*Mov64)(nil) 31 | 32 | type Mov64 struct { 33 | ebpf.Mov64 34 | } 35 | 36 | func (i *Mov64) Clone() Instruction { 37 | c := *i 38 | return &c 39 | } 40 | 41 | func (i *Mov64) Execute(vm *VM) error { 42 | err := vm.Registers.Assign(i.Dest, newIMM(int64(i.Value))) 43 | if err != nil { 44 | return fmt.Errorf("assign %s: %w", i.Dest.String(), err) 45 | } 46 | 47 | return nil 48 | } 49 | 50 | var _ Instruction = (*Mov32Register)(nil) 51 | 52 | type Mov32Register struct { 53 | ebpf.Mov32Register 54 | } 55 | 56 | func (i *Mov32Register) Clone() Instruction { 57 | c := *i 58 | return &c 59 | } 60 | 61 | func (i *Mov32Register) Execute(vm *VM) error { 62 | dr, err := vm.Registers.Copy(i.Src) 63 | if err != nil { 64 | return fmt.Errorf("read %s: %w", i.Dest.String(), err) 65 | } 66 | 67 | // TODO is this correct, or should we preseve the upper 32 bits? 68 | err = vm.Registers.Assign(i.Dest, dr) 69 | if err != nil { 70 | return fmt.Errorf("assign %s: %w", i.Dest.String(), err) 71 | } 72 | 73 | return nil 74 | } 75 | 76 | var _ Instruction = (*Mov64Register)(nil) 77 | 78 | type Mov64Register struct { 79 | ebpf.Mov64Register 80 | } 81 | 82 | func (i *Mov64Register) Clone() Instruction { 83 | c := *i 84 | return &c 85 | } 86 | 87 | func (i *Mov64Register) Execute(vm *VM) error { 88 | dr, err := vm.Registers.Copy(i.Src) 89 | if err != nil { 90 | return fmt.Errorf("read %s: %w", i.Dest.String(), err) 91 | } 92 | 93 | err = vm.Registers.Assign(i.Dest, dr) 94 | if err != nil { 95 | return fmt.Errorf("assign %s: %w", i.Dest.String(), err) 96 | } 97 | 98 | return nil 99 | } 100 | -------------------------------------------------------------------------------- /emulator/inst_mul.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Mul32)(nil) 10 | 11 | type Mul32 struct { 12 | ebpf.Mul32 13 | } 14 | 15 | func (i *Mul32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Mul32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) * i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Mul64)(nil) 35 | 36 | type Mul64 struct { 37 | ebpf.Mul64 38 | } 39 | 40 | func (i *Mul64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Mul64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv * int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Mul32Register)(nil) 60 | 61 | type Mul32Register struct { 62 | ebpf.Mul32Register 63 | } 64 | 65 | func (i *Mul32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Mul32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) * int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Mul64Register)(nil) 90 | 91 | type Mul64Register struct { 92 | ebpf.Mul64Register 93 | } 94 | 95 | func (i *Mul64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Mul64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv * sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_neg.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Neg32)(nil) 10 | 11 | type Neg32 struct { 12 | ebpf.Neg32 13 | } 14 | 15 | func (i *Neg32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Neg32) Execute(vm *VM) error { 21 | dv, dr, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = dr.Assign(int64(-int32(dv))) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Neg64)(nil) 35 | 36 | type Neg64 struct { 37 | ebpf.Neg64 38 | } 39 | 40 | func (i *Neg64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Neg64) Execute(vm *VM) error { 46 | dv, dr, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = dr.Assign(-dv) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /emulator/inst_nop.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "github.com/dylandreimerink/gobpfld/ebpf" 5 | ) 6 | 7 | var _ Instruction = (*Nop)(nil) 8 | 9 | type Nop struct { 10 | ebpf.Nop 11 | } 12 | 13 | func (i *Nop) Clone() Instruction { 14 | c := *i 15 | return &c 16 | } 17 | 18 | func (i *Nop) Execute(vm *VM) error { 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /emulator/inst_or.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Or32)(nil) 10 | 11 | type Or32 struct { 12 | ebpf.Or32 13 | } 14 | 15 | func (i *Or32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Or32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) | i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Or64)(nil) 35 | 36 | type Or64 struct { 37 | ebpf.Or64 38 | } 39 | 40 | func (i *Or64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Or64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv | int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Or32Register)(nil) 60 | 61 | type Or32Register struct { 62 | ebpf.Or32Register 63 | } 64 | 65 | func (i *Or32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Or32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) | int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Or64Register)(nil) 90 | 91 | type Or64Register struct { 92 | ebpf.Or64Register 93 | } 94 | 95 | func (i *Or64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Or64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv | sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_rsh.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Rsh32)(nil) 10 | 11 | type Rsh32 struct { 12 | ebpf.Rsh32 13 | } 14 | 15 | func (i *Rsh32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Rsh32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(uint32(rv) >> i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Rsh64)(nil) 35 | 36 | type Rsh64 struct { 37 | ebpf.Rsh64 38 | } 39 | 40 | func (i *Rsh64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Rsh64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(int64(uint64(rv) >> i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Rsh32Register)(nil) 60 | 61 | type Rsh32Register struct { 62 | ebpf.Rsh32Register 63 | } 64 | 65 | func (i *Rsh32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Rsh32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(uint32(dv) >> int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Rsh64Register)(nil) 90 | 91 | type Rsh64Register struct { 92 | ebpf.Rsh64Register 93 | } 94 | 95 | func (i *Rsh64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Rsh64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(int64(uint64(dv) >> sv)) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_store.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*StoreMemoryConstant)(nil) 10 | 11 | type StoreMemoryConstant struct { 12 | ebpf.StoreMemoryConstant 13 | } 14 | 15 | func (i *StoreMemoryConstant) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *StoreMemoryConstant) Execute(vm *VM) error { 21 | sv := newIMM(int64(i.Value)) 22 | 23 | dr, err := vm.Registers.Copy(i.Dest) 24 | if err != nil { 25 | return fmt.Errorf("copy %s: %w", i.Dest, err) 26 | } 27 | 28 | var off int64 29 | var memory Memory 30 | switch dmp := dr.(type) { 31 | case *MemoryPtr: 32 | // Memory pointers point to the start of a memory block 33 | off = dmp.Offset + int64(i.Offset) 34 | memory = dmp.Memory 35 | 36 | case *FramePointer: 37 | // Frame pointers point to the end of a stack frame 38 | off = int64(dmp.Memory.Size()) + dmp.Offset + int64(i.Offset) 39 | memory = dmp.Memory 40 | 41 | default: 42 | return fmt.Errorf("can't store to a non-pointer register value") 43 | } 44 | 45 | err = memory.Write(int(off), sv, i.Size) 46 | if err != nil { 47 | return fmt.Errorf("memory write: %w", err) 48 | } 49 | 50 | return nil 51 | } 52 | 53 | var _ Instruction = (*StoreMemoryRegister)(nil) 54 | 55 | type StoreMemoryRegister struct { 56 | ebpf.StoreMemoryRegister 57 | } 58 | 59 | func (i *StoreMemoryRegister) Clone() Instruction { 60 | c := *i 61 | return &c 62 | } 63 | 64 | func (i *StoreMemoryRegister) Execute(vm *VM) error { 65 | sr, err := vm.Registers.Get(i.Src) 66 | if err != nil { 67 | return fmt.Errorf("get %s: %w", i.Src.String(), err) 68 | } 69 | sv := sr.Copy() 70 | 71 | dr, err := vm.Registers.Copy(i.Dest) 72 | if err != nil { 73 | return fmt.Errorf("copy %s: %w", i.Dest, err) 74 | } 75 | 76 | var off int64 77 | var memory Memory 78 | switch dmp := dr.(type) { 79 | case *MemoryPtr: 80 | // Memory pointers point to the start of a memory block 81 | off = dmp.Offset + int64(i.Offset) 82 | memory = dmp.Memory 83 | 84 | case *FramePointer: 85 | // Frame pointers point to the end of a stack frame 86 | off = int64(dmp.Memory.Size()) + dmp.Offset + int64(i.Offset) 87 | memory = dmp.Memory 88 | 89 | default: 90 | return fmt.Errorf("can't store to a non-pointer register value") 91 | } 92 | 93 | err = memory.Write(int(off), sv, i.Size) 94 | if err != nil { 95 | return fmt.Errorf("memory write: %w", err) 96 | } 97 | 98 | return nil 99 | } 100 | -------------------------------------------------------------------------------- /emulator/inst_sub.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Sub32)(nil) 10 | 11 | type Sub32 struct { 12 | ebpf.Sub32 13 | } 14 | 15 | func (i *Sub32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Sub32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) - i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Sub64)(nil) 35 | 36 | type Sub64 struct { 37 | ebpf.Sub64 38 | } 39 | 40 | func (i *Sub64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Sub64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv - int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Sub32Register)(nil) 60 | 61 | type Sub32Register struct { 62 | ebpf.Sub32Register 63 | } 64 | 65 | func (i *Sub32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Sub32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) - int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Sub64Register)(nil) 90 | 91 | type Sub64Register struct { 92 | ebpf.Sub64Register 93 | } 94 | 95 | func (i *Sub64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Sub64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv - sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/inst_xor.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | var _ Instruction = (*Xor32)(nil) 10 | 11 | type Xor32 struct { 12 | ebpf.Xor32 13 | } 14 | 15 | func (i *Xor32) Clone() Instruction { 16 | c := *i 17 | return &c 18 | } 19 | 20 | func (i *Xor32) Execute(vm *VM) error { 21 | rv, r, err := readReg(vm, i.Dest) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | err = r.Assign(int64(int32(rv) ^ i.Value)) 27 | if err != nil { 28 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | var _ Instruction = (*Xor64)(nil) 35 | 36 | type Xor64 struct { 37 | ebpf.Xor64 38 | } 39 | 40 | func (i *Xor64) Clone() Instruction { 41 | c := *i 42 | return &c 43 | } 44 | 45 | func (i *Xor64) Execute(vm *VM) error { 46 | rv, r, err := readReg(vm, i.Dest) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | err = r.Assign(rv ^ int64(i.Value)) 52 | if err != nil { 53 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | var _ Instruction = (*Xor32Register)(nil) 60 | 61 | type Xor32Register struct { 62 | ebpf.Xor32Register 63 | } 64 | 65 | func (i *Xor32Register) Clone() Instruction { 66 | c := *i 67 | return &c 68 | } 69 | 70 | func (i *Xor32Register) Execute(vm *VM) error { 71 | dv, dr, err := readReg(vm, i.Dest) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | sv, _, err := readReg(vm, i.Src) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = dr.Assign(int64(int32(dv) ^ int32(sv))) 82 | if err != nil { 83 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | var _ Instruction = (*Xor64Register)(nil) 90 | 91 | type Xor64Register struct { 92 | ebpf.Xor64Register 93 | } 94 | 95 | func (i *Xor64Register) Clone() Instruction { 96 | c := *i 97 | return &c 98 | } 99 | 100 | func (i *Xor64Register) Execute(vm *VM) error { 101 | dv, dr, err := readReg(vm, i.Dest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | sv, _, err := readReg(vm, i.Src) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | err = dr.Assign(dv ^ sv) 112 | if err != nil { 113 | return fmt.Errorf("assign value %s: %w", i.Dest.String(), err) 114 | } 115 | 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /emulator/linux_errno.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | // These error codes are defined by linux and used to return errors from eBPF helper calls 4 | // https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/errno-base.h 5 | 6 | // Permission denied 7 | func eperm() *IMMValue { 8 | return newIMM(-1) 9 | } 10 | 11 | // Argument list too long 12 | func e2big() *IMMValue { 13 | return newIMM(-7) 14 | } 15 | 16 | // Bad address 17 | func efault() *IMMValue { 18 | return newIMM(-14) 19 | } 20 | -------------------------------------------------------------------------------- /emulator/maps_hash_lru_test.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "crypto/sha256" 5 | "testing" 6 | 7 | "github.com/dylandreimerink/gobpfld" 8 | "github.com/dylandreimerink/gobpfld/bpfsys" 9 | "github.com/dylandreimerink/gobpfld/bpftypes" 10 | ) 11 | 12 | func TestEmulatedHashMapLRU(t *testing.T) { 13 | m := HashMapLRU{ 14 | AbstractMap: AbstractMap{ 15 | Name: "LRU map", 16 | Def: gobpfld.BPFMapDef{ 17 | Type: bpftypes.BPF_MAP_TYPE_LRU_HASH, 18 | KeySize: 4, 19 | ValueSize: 4, 20 | MaxEntries: 5, 21 | }, 22 | }, 23 | } 24 | 25 | err := m.Init() 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | makeMemPtr := func(i int) *MemoryPtr { 31 | im := newIMM(int64(i)) 32 | return &MemoryPtr{ 33 | Memory: &ValueMemory{ 34 | Mapping: []RegisterValue{ 35 | im, 36 | im, 37 | im, 38 | im, 39 | }, 40 | }, 41 | } 42 | } 43 | 44 | k1 := makeMemPtr(1) 45 | k2 := makeMemPtr(2) 46 | k3 := makeMemPtr(3) 47 | k4 := makeMemPtr(4) 48 | k5 := makeMemPtr(5) 49 | k6 := makeMemPtr(6) 50 | 51 | // Add 5 values, filling the map 52 | _, err = m.Update(k1, makeMemPtr(11), bpfsys.BPFMapElemAny) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | _, err = m.Update(k2, makeMemPtr(12), bpfsys.BPFMapElemAny) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | _, err = m.Update(k3, makeMemPtr(13), bpfsys.BPFMapElemAny) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | _, err = m.Update(k4, makeMemPtr(14), bpfsys.BPFMapElemAny) 65 | if err != nil { 66 | t.Fatal(err) 67 | } 68 | _, err = m.Update(k5, makeMemPtr(15), bpfsys.BPFMapElemAny) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | // Key 1 was added first, so it is least recently used. 74 | 75 | // Lookup 1 and 2, no 3 should be least recently used 76 | _, err = m.Lookup(k1) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | _, err = m.Lookup(k2) 81 | if err != nil { 82 | t.Fatal(err) 83 | } 84 | 85 | // Insert a sixth element, which should overwrite 3 86 | _, err = m.Update(k6, makeMemPtr(16), bpfsys.BPFMapElemAny) 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | 91 | kh := func(key *MemoryPtr) hashKey { 92 | b, err := key.ReadRange(0, 4) 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | 97 | return sha256.Sum256(b) 98 | } 99 | 100 | k1h := kh(k1) 101 | k2h := kh(k2) 102 | k4h := kh(k4) 103 | k5h := kh(k5) 104 | k6h := kh(k6) 105 | 106 | // 6 should be the most recent since it was added last 107 | if m.UsageList[0] != k6h { 108 | t.Fatal("usage list 0 != k6h") 109 | } 110 | // 2 should be second due to the most recent lookup 111 | if m.UsageList[1] != k2h { 112 | t.Fatal("usage list 0 != k2h") 113 | } 114 | // 1 should be third due to the lookup 115 | if m.UsageList[2] != k1h { 116 | t.Fatal("usage list 0 != k1h") 117 | } 118 | if m.UsageList[3] != k5h { 119 | t.Fatal("usage list 0 != k5h") 120 | } 121 | if m.UsageList[4] != k4h { 122 | t.Fatal("usage list 0 != k4h") 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /emulator/maps_perf_event_array.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/bpfsys" 7 | "github.com/dylandreimerink/gobpfld/ebpf" 8 | ) 9 | 10 | // PerfEventArray is the emulated implementation of BPF_MAP_TYPE_PERF_EVENT_ARRAY. In linux this is a pseudo map type 11 | // which is not actually a map, rater a way to one-way data stream using the perf sub system. The userspace program 12 | // would have to consume data while it is being produced or risk losing data if the buffer is full. 13 | // 14 | // This emulated map type has to be consumed by the caller of virtual machine, the event data is stored in a slice 15 | // which is currently unlimited in size, it can be read and consumed just like an array map. 16 | type PerfEventArray struct { 17 | AbstractMap 18 | 19 | Events [][]byte 20 | } 21 | 22 | func (m *PerfEventArray) Init() error { 23 | return nil 24 | } 25 | 26 | func (m *PerfEventArray) Keys() []RegisterValue { 27 | keys := make([]RegisterValue, len(m.Events)) 28 | for i := range keys { 29 | imm := newIMM(int64(i)) 30 | keys[i] = &MemoryPtr{ 31 | Memory: &ValueMemory{ 32 | MemName: fmt.Sprintf("%s[%d]", m.Name, m.Def.ValueSize), 33 | Mapping: []RegisterValue{ 34 | imm, 35 | imm, 36 | imm, 37 | imm, 38 | }, 39 | }, 40 | } 41 | } 42 | return keys 43 | } 44 | 45 | func (m *PerfEventArray) Lookup(key RegisterValue) (RegisterValue, error) { 46 | keyPtr, ok := key.(PointerValue) 47 | if !ok { 48 | return nil, errMapKeyNoPtr 49 | } 50 | 51 | keyValReg, err := keyPtr.Deref(0, ebpf.BPF_W) 52 | if !ok { 53 | return nil, fmt.Errorf("key pointer deref: %w", err) 54 | } 55 | 56 | kv := keyValReg.Value() 57 | if int(kv) >= len(m.Events) { 58 | return newIMM(0), nil 59 | } 60 | 61 | return &MemoryPtr{Memory: &ByteMemory{ 62 | MemName: fmt.Sprintf("%s[%d]", m.Name, kv), 63 | Backing: m.Events[kv], 64 | }, Offset: 0}, nil 65 | } 66 | 67 | func (m *PerfEventArray) Update( 68 | key RegisterValue, 69 | value RegisterValue, 70 | flags bpfsys.BPFAttrMapElemFlags, 71 | ) ( 72 | RegisterValue, 73 | error, 74 | ) { 75 | // Not allowed to update values, since this kind of map is a sort of stream, not an actual map 76 | return nil, errMapNotImplemented 77 | } 78 | 79 | func (m *PerfEventArray) Delete(key RegisterValue, flags bpfsys.BPFAttrMapElemFlags) error { 80 | keyPtr, ok := key.(PointerValue) 81 | if !ok { 82 | return errMapKeyNoPtr 83 | } 84 | 85 | keyValReg, err := keyPtr.Deref(0, ebpf.BPF_W) 86 | if !ok { 87 | return fmt.Errorf("key pointer deref: %w", err) 88 | } 89 | 90 | kv := keyValReg.Value() 91 | if int(kv) >= len(m.Events) { 92 | return nil 93 | } 94 | 95 | copy(m.Events[kv:], m.Events[kv+1:]) 96 | m.Events = m.Events[:len(m.Events)-1] 97 | 98 | return nil 99 | } 100 | 101 | func (m *PerfEventArray) Push(value RegisterValue, size int64) error { 102 | valuePtr, ok := value.(PointerValue) 103 | if !ok { 104 | return errMapKeyNoPtr 105 | } 106 | 107 | val, err := valuePtr.ReadRange(0, int(size)) 108 | if !ok { 109 | return fmt.Errorf("value pointer read range: %w", err) 110 | } 111 | 112 | m.Events = append(m.Events, val) 113 | 114 | return nil 115 | } 116 | -------------------------------------------------------------------------------- /emulator/maps_queue.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | type QueueMap struct { 10 | AbstractMap 11 | 12 | // TODO this is a very slow an inefficient way to implement a queue. Should use a ringbuffer instread 13 | Values []*ByteMemory 14 | } 15 | 16 | func (m *QueueMap) Init() error { 17 | return nil 18 | } 19 | 20 | func (m *QueueMap) Keys() []RegisterValue { 21 | keys := make([]RegisterValue, len(m.Values)) 22 | for i := range keys { 23 | imm := newIMM(int64(i)) 24 | keys[i] = &MemoryPtr{ 25 | Memory: &ValueMemory{ 26 | MemName: fmt.Sprintf("%s[%d]", m.Name, m.Def.ValueSize), 27 | Mapping: []RegisterValue{ 28 | imm, 29 | imm, 30 | imm, 31 | imm, 32 | }, 33 | }, 34 | } 35 | } 36 | return keys 37 | } 38 | 39 | func (m *QueueMap) Lookup(key RegisterValue) (RegisterValue, error) { 40 | keyPtr, ok := key.(PointerValue) 41 | if !ok { 42 | return nil, errMapKeyNoPtr 43 | } 44 | 45 | keyVal, err := keyPtr.Deref(0, ebpf.BPF_W) 46 | if !ok { 47 | return nil, fmt.Errorf("key read range: %w", err) 48 | } 49 | 50 | keyIndex := int(keyVal.Value()) 51 | if keyIndex < 0 || keyIndex >= len(m.Values) { 52 | return nil, errMapOutOfMemory 53 | } 54 | 55 | value := m.Values[keyIndex] 56 | 57 | return &MemoryPtr{Memory: value, Offset: 0}, nil 58 | } 59 | 60 | func (m *QueueMap) Push(value RegisterValue, size int64) error { 61 | valuePtr, ok := value.(PointerValue) 62 | if !ok { 63 | return errMapValNoPtr 64 | } 65 | 66 | valueVal, err := valuePtr.ReadRange(0, int(size)) 67 | if !ok { 68 | return fmt.Errorf("value read range: %w", err) 69 | } 70 | 71 | m.Values = append(m.Values, &ByteMemory{ 72 | MemName: fmt.Sprintf("%s[?]", m.Name), 73 | Backing: valueVal, 74 | }) 75 | 76 | return nil 77 | } 78 | 79 | func (m *QueueMap) Pop() (RegisterValue, error) { 80 | if len(m.Values) == 0 { 81 | return newIMM(0), nil 82 | } 83 | 84 | val := m.Values[0] 85 | copy(m.Values, m.Values[1:]) 86 | m.Values = m.Values[:len(m.Values)-1] 87 | 88 | return &MemoryPtr{ 89 | Memory: val, 90 | }, nil 91 | } 92 | -------------------------------------------------------------------------------- /emulator/maps_stack.go: -------------------------------------------------------------------------------- 1 | package emulator 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/ebpf" 7 | ) 8 | 9 | type StackMap struct { 10 | AbstractMap 11 | 12 | Values []*ByteMemory 13 | } 14 | 15 | func (m *StackMap) Init() error { 16 | return nil 17 | } 18 | 19 | func (m *StackMap) Keys() []RegisterValue { 20 | keys := make([]RegisterValue, len(m.Values)) 21 | for i := range keys { 22 | imm := newIMM(int64(i)) 23 | keys[i] = &MemoryPtr{ 24 | Memory: &ValueMemory{ 25 | MemName: fmt.Sprintf("%s[%d]", m.Name, m.Def.ValueSize), 26 | Mapping: []RegisterValue{ 27 | imm, 28 | imm, 29 | imm, 30 | imm, 31 | }, 32 | }, 33 | } 34 | } 35 | return keys 36 | } 37 | 38 | func (m *StackMap) Lookup(key RegisterValue) (RegisterValue, error) { 39 | keyPtr, ok := key.(PointerValue) 40 | if !ok { 41 | return nil, errMapKeyNoPtr 42 | } 43 | 44 | keyVal, err := keyPtr.Deref(0, ebpf.BPF_W) 45 | if !ok { 46 | return nil, fmt.Errorf("key read range: %w", err) 47 | } 48 | 49 | keyIndex := int(keyVal.Value()) 50 | if keyIndex < 0 || keyIndex >= len(m.Values) { 51 | return nil, errMapOutOfMemory 52 | } 53 | 54 | // Since the stack grows down(new items at the bottom), index 0 is the last value. 55 | value := m.Values[len(m.Values)-1-keyIndex] 56 | 57 | return &MemoryPtr{Memory: value, Offset: 0}, nil 58 | } 59 | 60 | func (m *StackMap) Push(value RegisterValue, size int64) error { 61 | valuePtr, ok := value.(PointerValue) 62 | if !ok { 63 | return errMapValNoPtr 64 | } 65 | 66 | valueVal, err := valuePtr.ReadRange(0, int(size)) 67 | if !ok { 68 | return fmt.Errorf("value read range: %w", err) 69 | } 70 | 71 | m.Values = append(m.Values, &ByteMemory{ 72 | MemName: fmt.Sprintf("%s[?]", m.Name), 73 | Backing: valueVal, 74 | }) 75 | 76 | return nil 77 | } 78 | 79 | func (m *StackMap) Pop() (RegisterValue, error) { 80 | if len(m.Values) == 0 { 81 | return newIMM(0), nil 82 | } 83 | 84 | val := m.Values[len(m.Values)-1] 85 | m.Values = m.Values[:len(m.Values)-1] 86 | 87 | return &MemoryPtr{ 88 | Memory: val, 89 | }, nil 90 | } 91 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dylandreimerink/gobpfld 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/alecthomas/participle/v2 v2.0.0-alpha6 7 | github.com/dylandreimerink/gocovmerge v1.0.0 8 | github.com/dylandreimerink/tarp v0.1.2 9 | github.com/spf13/cobra v1.3.0 10 | github.com/vishvananda/netlink v1.1.0 11 | golang.org/x/sys v0.0.0-20211205182925-97ca703d548d 12 | golang.org/x/tools v0.1.8 13 | ) 14 | -------------------------------------------------------------------------------- /internal/cstr/cstring.go: -------------------------------------------------------------------------------- 1 | package cstr 2 | 3 | import "strings" 4 | 5 | // ToString trims the string at the first null byte which is used in C to indicate the end of the string 6 | func ToString(cstr string) string { 7 | nbi := strings.IndexByte(cstr, 0x00) 8 | if nbi != -1 { 9 | return cstr[:nbi] 10 | } 11 | return cstr 12 | } 13 | 14 | // BytesToString converts bytes to string assuming it is a C string 15 | func BytesToString(b []byte) string { 16 | return ToString(string(b)) 17 | } 18 | 19 | // StringToCStrBytes turns the string into a null terminated byte slice 20 | func StringToCStrBytes(str string) []byte { 21 | return []byte(str + "\x00") 22 | } 23 | -------------------------------------------------------------------------------- /internal/syscall/bind.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | type Socklen uint32 10 | 11 | // Bind is a public version of the unix.bind without additional wrappers which allows us to use any 12 | // value type we want. But does require the usage of unsafe. 13 | func Bind(s int, addr unsafe.Pointer, addrlen Socklen) (err error) { 14 | _, _, e1 := unix.Syscall(unix.SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) 15 | if e1 != 0 { 16 | err = &Error{ 17 | Errno: e1, 18 | } 19 | } 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /internal/syscall/bpf.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | type BPFAttribute interface { 10 | ToPtr() unsafe.Pointer 11 | Size() uintptr 12 | } 13 | 14 | // Bpf is a wrapper around the BPF syscall, so a very low level function. 15 | func Bpf(cmd int, attr BPFAttribute, size int) (fd uintptr, err error) { 16 | fd, _, errno := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr.ToPtr()), uintptr(size)) 17 | if errno != 0 { 18 | err = &Error{ 19 | Errno: errno, 20 | } 21 | } 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /internal/syscall/ioctl.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | func IOCtl(fd int, req uint, arg uintptr) (err error) { 6 | _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), arg) 7 | if e1 != 0 { 8 | err = &Error{ 9 | Errno: e1, 10 | } 11 | } 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /internal/syscall/sendto.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // Zero single-word zero for use when we need a valid pointer to 0 bytes. 10 | // See mkunix.pl. 11 | var Zero uintptr 12 | 13 | // Sendto is a public version of the unix.sendto without additional wrappers which allows us to use any 14 | // value type we want. But does require the usage of unsafe. 15 | func Sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen Socklen) (err error) { 16 | var _p0 unsafe.Pointer 17 | if len(buf) > 0 { 18 | _p0 = unsafe.Pointer(&buf[0]) 19 | } else { 20 | _p0 = unsafe.Pointer(&Zero) 21 | } 22 | _, _, e1 := unix.Syscall6( 23 | unix.SYS_SENDTO, 24 | uintptr(s), 25 | uintptr(_p0), 26 | uintptr(len(buf)), 27 | uintptr(flags), 28 | uintptr(to), 29 | uintptr(addrlen), 30 | ) 31 | if e1 != 0 { 32 | err = &Error{ 33 | Errno: e1, 34 | } 35 | } 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /internal/syscall/sockopt.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // Getsockopt is a public version of the unix.getsockopt without additional wrappers which allows us to use any 10 | // value type we want. But does require the usage of unsafe. 11 | func Getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *Socklen) (err error) { 12 | _, _, e1 := unix.Syscall6( 13 | unix.SYS_GETSOCKOPT, 14 | uintptr(s), 15 | uintptr(level), 16 | uintptr(name), 17 | uintptr(val), 18 | uintptr(unsafe.Pointer(vallen)), 19 | 0, 20 | ) 21 | if e1 != 0 { 22 | err = &Error{ 23 | Errno: e1, 24 | } 25 | } 26 | return 27 | } 28 | 29 | // Setsockopt is a public version of the unix.setsockopt without additional wrappers which allows us to use any 30 | // value type we want. But does require the usage of unsafe. 31 | func Setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { 32 | _, _, e1 := unix.Syscall6( 33 | unix.SYS_SETSOCKOPT, 34 | uintptr(s), 35 | uintptr(level), 36 | uintptr(name), 37 | uintptr(val), 38 | vallen, 39 | 0, 40 | ) 41 | if e1 != 0 { 42 | err = &Error{ 43 | Errno: e1, 44 | } 45 | } 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /internal/syscall/syscall.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // ENOTSUPP - Operation is not supported 10 | const ENOTSUPP = unix.Errno(524) 11 | 12 | // a map of string translations for syscall errors which are no included in the standard library 13 | var nonStdErrors = map[unix.Errno]string{ 14 | ENOTSUPP: "Operation is not supported", 15 | } 16 | 17 | // Error is an error wrapper for syscall errors 18 | type Error struct { 19 | // Context specific error information since the same code can have different 20 | // meaning depending on context 21 | Err string 22 | // The underlaying syscall error number 23 | Errno unix.Errno 24 | } 25 | 26 | func (e *Error) Error() string { 27 | errStr := nonStdErrors[e.Errno] 28 | if errStr == "" { 29 | errStr = e.Errno.Error() 30 | } 31 | 32 | if e.Err == "" { 33 | return fmt.Sprintf("%s (%d)", errStr, e.Errno) 34 | } 35 | 36 | return fmt.Sprintf("%s (%s)(%d)", e.Err, errStr, e.Errno) 37 | } 38 | -------------------------------------------------------------------------------- /iterator_benchmark_test.go: -------------------------------------------------------------------------------- 1 | //go:build bpftests 2 | // +build bpftests 3 | 4 | package gobpfld 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/dylandreimerink/gobpfld/bpfsys" 10 | "github.com/dylandreimerink/gobpfld/bpftypes" 11 | ) 12 | 13 | const mapSize = 100000 14 | 15 | var testMap *HashMap 16 | 17 | func getTestMap() *HashMap { 18 | if testMap != nil { 19 | return testMap 20 | } 21 | 22 | testMap = &HashMap{ 23 | AbstractMap: AbstractMap{ 24 | Name: MustNewObjName("xdp_stats_map"), 25 | Definition: BPFMapDef{ 26 | Type: bpftypes.BPF_MAP_TYPE_ARRAY, 27 | KeySize: 4, // SizeOf(uint32) 28 | ValueSize: 8, // SizeOf(uint64) 29 | MaxEntries: mapSize, 30 | }, 31 | }, 32 | } 33 | 34 | err := testMap.Load() 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | for i := uint32(0); i < mapSize; i++ { 40 | val := uint64(i * 10) 41 | err = testMap.Set(&i, &val, bpfsys.BPFMapElemAny) 42 | if err != nil { 43 | panic(err) 44 | } 45 | } 46 | 47 | return testMap 48 | } 49 | 50 | func benchmarkBatchMapIterator(bufSize int, b *testing.B) { 51 | tMap := getTestMap() 52 | 53 | b.ResetTimer() 54 | 55 | // run the Fib function b.N times 56 | for n := 0; n < b.N; n++ { 57 | iter := batchLookupIterator{ 58 | BPFMap: tMap, 59 | BufSize: bufSize, 60 | } 61 | 62 | var ( 63 | key uint32 64 | value uint64 65 | ) 66 | err := iter.Init(&key, &value) 67 | if err != nil { 68 | b.Error(err) 69 | } 70 | 71 | var updated bool 72 | for updated, err = iter.Next(); updated && err == nil; updated, err = iter.Next() { 73 | } 74 | if err != nil { 75 | b.Error(err) 76 | } 77 | } 78 | } 79 | 80 | func BenchmarkBatchMapIterator16(b *testing.B) { 81 | benchmarkBatchMapIterator(16, b) 82 | } 83 | 84 | func BenchmarkBatchMapIterator64(b *testing.B) { 85 | benchmarkBatchMapIterator(64, b) 86 | } 87 | 88 | func BenchmarkBatchMapIterator256(b *testing.B) { 89 | benchmarkBatchMapIterator(256, b) 90 | } 91 | 92 | func BenchmarkBatchMapIterator1024(b *testing.B) { 93 | benchmarkBatchMapIterator(1024, b) 94 | } 95 | 96 | func BenchmarkBatchMapIterator4096(b *testing.B) { 97 | benchmarkBatchMapIterator(4096, b) 98 | } 99 | 100 | func BenchmarkBatchMapIterator16384(b *testing.B) { 101 | benchmarkBatchMapIterator(16384, b) 102 | } 103 | -------------------------------------------------------------------------------- /kernel-changes.md: -------------------------------------------------------------------------------- 1 | # A list of kernel change regarding BPF 2 | 3 | This library should stay up-to-date with the latest kernel changes, so this is a place to track recent changes that came to my attention. 4 | 5 | - Add lookup_and_delete_elem support to BPF hash map types https://lwn.net/Articles/856017/ 6 | - bpf: syscall program, FD array, loader program, light skeleton. https://lwn.net/Articles/856191/ 7 | - eBPF seccomp filters https://lwn.net/Articles/855970/ 8 | - Add TC-BPF API https://lwn.net/Articles/856041/ 9 | - sockmap: add sockmap support to Unix datagram socket https://lwn.net/Articles/853466/ 10 | - Making eBPF work on Windows https://cloudblogs.microsoft.com/opensource/2021/05/10/making-ebpf-work-on-windows/ 11 | 12 | ## Links to keep an eye on 13 | 14 | - https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/log/ 15 | - https://lwn.net/Kernel/Patches and https://lwn.net/ 16 | - https://lwn.net/Articles/ -------------------------------------------------------------------------------- /kernelsupport/arch.go: -------------------------------------------------------------------------------- 1 | package kernelsupport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // ArchSupport is a flagset which describes on which architectures eBPF is supported 9 | type ArchSupport uint64 10 | 11 | const ( 12 | // KFeatArchx86_64 means the kernel has eBPF support on the x86_64 architecture 13 | KFeatArchx86_64 ArchSupport = 1 << iota 14 | // KFeatArchARM64 means the kernel has eBPF support on the ARM64 architecture 15 | KFeatArchARM64 16 | // KFeatArchs390 means the kernel has eBPF support on the s390 architecture 17 | KFeatArchs390 18 | // KFeatArchPP64 means the kernel has eBPF support on the PowerPC64 architecture 19 | KFeatArchPP64 20 | // KFeatArchSparc64 means the kernel has eBPF support on the Sparc64 architecture 21 | KFeatArchSparc64 22 | // KFeatArchMIPS means the kernel has eBPF support on the MIPS architecture 23 | KFeatArchMIPS 24 | // KFeatArchARM32 means the kernel has eBPF support on the ARM32 architecture 25 | KFeatArchARM32 26 | // KFeatArchx86 means the kernel has eBPF support on the x86_32 architecture 27 | KFeatArchx86 28 | // KFeatArchRiscVRV64G means the kernel has eBPF support on the RISC-V RV64G architecture 29 | KFeatArchRiscVRV64G 30 | // KFeatArchRiscVRV32G means the kernel has eBPF support on the RISC-V RV32G architecture 31 | KFeatArchRiscVRV32G 32 | 33 | // An end marker for enumeration, not an actual feature flag 34 | kFeatArchMax //nolint:revive // leading k is used to stay consistent with exported vars 35 | ) 36 | 37 | // Has returns true if 'as' has all the specified flags 38 | func (as ArchSupport) Has(flags ArchSupport) bool { 39 | return as&flags == flags 40 | } 41 | 42 | var archSupportToString = map[ArchSupport]string{ 43 | KFeatArchx86_64: "x86_64", 44 | KFeatArchARM64: "ARM64", 45 | KFeatArchs390: "s309", 46 | KFeatArchPP64: "PowerPC64", 47 | KFeatArchSparc64: "Sparc64", 48 | KFeatArchMIPS: "MIPS", 49 | KFeatArchARM32: "ARM32", 50 | KFeatArchx86: "x86_32", 51 | KFeatArchRiscVRV64G: "RISC-V RV64G", 52 | KFeatArchRiscVRV32G: "RISC-V RV32G", 53 | } 54 | 55 | func (as ArchSupport) String() string { 56 | var archs []string 57 | for i := ArchSupport(1); i < kFeatArchMax; i = i << 1 { 58 | // If this flag is set 59 | if as&i > 0 { 60 | archStr := archSupportToString[i] 61 | if archStr == "" { 62 | archStr = fmt.Sprintf("missing arch str(%d)", i) 63 | } 64 | archs = append(archs, archStr) 65 | } 66 | } 67 | 68 | if len(archs) == 0 { 69 | return "No support" 70 | } 71 | 72 | if len(archs) == 1 { 73 | return archs[0] 74 | } 75 | 76 | return strings.Join(archs, ", ") 77 | } 78 | -------------------------------------------------------------------------------- /kernelsupport/doc.go: -------------------------------------------------------------------------------- 1 | // package kernelsupport is used to query what eBPF features are supported for different version of the linux kernel 2 | package kernelsupport 3 | -------------------------------------------------------------------------------- /kernelsupport/kernelsupport_test.go: -------------------------------------------------------------------------------- 1 | package kernelsupport 2 | 3 | import "testing" 4 | 5 | func Test_kernelVersion_Higher(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | a KernelVersion 9 | b KernelVersion 10 | want bool 11 | }{ 12 | { 13 | name: "2.0.0 >= 1.0.0 - major", 14 | a: KernelVersion{Major: 2}, 15 | b: KernelVersion{Major: 1}, 16 | want: true, 17 | }, 18 | { 19 | name: "2.1.0 >= 2.0.0 - minor", 20 | a: KernelVersion{Major: 2, Minor: 1}, 21 | b: KernelVersion{Major: 2}, 22 | want: true, 23 | }, 24 | { 25 | name: "2.1.1 >= 2.1.0 - patch", 26 | a: KernelVersion{Major: 2, Minor: 1, Patch: 1}, 27 | b: KernelVersion{Major: 2, Minor: 1}, 28 | want: true, 29 | }, 30 | { 31 | name: "2.2.2 >= 2.2.2 - exact", 32 | a: KernelVersion{Major: 2, Minor: 2, Patch: 2}, 33 | b: KernelVersion{Major: 2, Minor: 2, Patch: 2}, 34 | want: true, 35 | }, 36 | { 37 | name: "1.1.0 >= 2.0.0 - major false", 38 | a: KernelVersion{Major: 1, Minor: 1}, 39 | b: KernelVersion{Major: 2}, 40 | want: false, 41 | }, 42 | { 43 | name: "2.1.0 >= 2.2.0 - minor false", 44 | a: KernelVersion{Major: 2, Minor: 1}, 45 | b: KernelVersion{Major: 2, Minor: 2}, 46 | want: false, 47 | }, 48 | { 49 | name: "2.2.0 >= 2.2.2 - patch false", 50 | a: KernelVersion{Major: 2, Minor: 2}, 51 | b: KernelVersion{Major: 2, Minor: 2, Patch: 1}, 52 | want: false, 53 | }, 54 | } 55 | for _, tt := range tests { 56 | t.Run(tt.name, func(t *testing.T) { 57 | if got := tt.a.Higher(tt.b); got != tt.want { 58 | t.Errorf("kernelVersion.Higher() = %v, want %v", got, tt.want) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kernelsupport/misc.go: -------------------------------------------------------------------------------- 1 | package kernelsupport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // MiscSupport is a flagset that describes features that don't neatly fit into any other category 9 | type MiscSupport uint64 10 | 11 | const ( 12 | // KFeatMiscXSKRingFlags indicates that the kernel version has flags in AF_XDP rings 13 | // https://github.com/torvalds/linux/commit/77cd0d7b3f257fd0e3096b4fdcff1a7d38e99e10 14 | KFeatMiscXSKRingFlags MiscSupport = iota 15 | // KFeatBTFFuncScope indicates that the kernel doesn't require BTF FUNC types to have a vlen of 0. 16 | // Since kernel 5.6, the scope of functions is encoded in vlen. 17 | // https://github.com/cilium/ebpf/issues/43 18 | // https://github.com/llvm/llvm-project/commit/fbb64aa69835c8e3e9efe0afc8a73058b5a0fb3c 19 | KFeatBTFFuncScope 20 | // KFeatGlobalData indicates that the kernel supports global data sections. 21 | // https://lwn.net/Articles/784936/ 22 | // https://github.com/torvalds/linux/commit/d8eca5bbb2be9bc7546f9e733786fa2f1a594c67 23 | KFeatGlobalData 24 | kFeatMiscMax //nolint:revive // leading k is used to stay consistent with exported vars 25 | ) 26 | 27 | // Has returns true if 'as' has all the specified flags 28 | func (ms MiscSupport) Has(flags MiscSupport) bool { 29 | return ms&flags == flags 30 | } 31 | 32 | var miscSupportToString = map[MiscSupport]string{ 33 | KFeatMiscXSKRingFlags: "XSK ring flags", 34 | } 35 | 36 | func (ms MiscSupport) String() string { 37 | var miscFeats []string 38 | for i := MiscSupport(1); i < kFeatMiscMax; i = i << 1 { 39 | // If this flag is set 40 | if ms&i > 0 { 41 | miscStr := miscSupportToString[i] 42 | if miscStr == "" { 43 | miscStr = fmt.Sprintf("missing attach str(%d)", i) 44 | } 45 | miscFeats = append(miscFeats, miscStr) 46 | } 47 | } 48 | 49 | if len(miscFeats) == 0 { 50 | return "No support" 51 | } 52 | 53 | if len(miscFeats) == 1 { 54 | return miscFeats[0] 55 | } 56 | 57 | return strings.Join(miscFeats, ", ") 58 | } 59 | -------------------------------------------------------------------------------- /map_data.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/dylandreimerink/gobpfld/bpfsys" 8 | "github.com/dylandreimerink/gobpfld/bpftypes" 9 | "github.com/dylandreimerink/gobpfld/kernelsupport" 10 | ) 11 | 12 | var _ BPFMap = (*dataMap)(nil) 13 | 14 | // dataMap is a special map type which is used to load relocated data from .data, .rodata and .bss sections. 15 | // It is actually an array map, but its contents are set just after loading, an this map has no exposed functions 16 | // to be used by users of the library. 17 | type dataMap struct { 18 | AbstractMap 19 | 20 | readOnly bool 21 | } 22 | 23 | func (m *dataMap) Load() error { 24 | err := m.load(func(attr *bpfsys.BPFAttrMapCreate) { 25 | attr.MapType = bpftypes.BPF_MAP_TYPE_ARRAY 26 | 27 | if m.readOnly { 28 | if kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapBPFRW) { 29 | attr.MapFlags |= bpftypes.BPFMapFlagsReadOnlyProg 30 | } 31 | } 32 | }) 33 | if err != nil { 34 | return fmt.Errorf("error while loading map: %w", err) 35 | } 36 | 37 | err = mapRegister.add(m) 38 | if err != nil { 39 | return fmt.Errorf("map register: %w", err) 40 | } 41 | 42 | // TODO move to abstract map load 43 | if m.InitialData[0] != nil { 44 | k := uint32(0) 45 | attr := bpfsys.BPFAttrMapElem{ 46 | MapFD: m.fd, 47 | Flags: bpfsys.BPFMapElemAny, 48 | } 49 | 50 | attr.Key, err = m.toKeyPtr(&k) 51 | if err != nil { 52 | return fmt.Errorf("unable to make ptr of uint32(0): %w", err) 53 | } 54 | 55 | if len(m.InitialData[0].([]byte)) != int(m.definition.ValueSize) { 56 | return fmt.Errorf( 57 | "initial data(%d) not of same size as map definition(%d)", 58 | len(m.InitialData), 59 | int(m.definition.ValueSize), 60 | ) 61 | } 62 | 63 | value, ok := m.InitialData[0].([]byte) 64 | if !ok { 65 | panic("initial data value of a dataMap isn't []byte") 66 | } 67 | attr.Value_NextKey = uintptr(unsafe.Pointer(&value[0])) 68 | 69 | err = bpfsys.MapUpdateElem(&attr) 70 | if err != nil { 71 | return fmt.Errorf("bpf syscall error: %w", err) 72 | } 73 | } 74 | 75 | // Freeze the map if it is supposed to be read only 76 | if m.readOnly && kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapFreeze) { 77 | err = bpfsys.MapFreeze(&bpfsys.BPFAttrMapElem{ 78 | MapFD: m.fd, 79 | }) 80 | if err != nil { 81 | return fmt.Errorf("freeze map: %w", err) 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | 88 | // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel 89 | // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map. 90 | func (m *dataMap) Close() error { 91 | err := mapRegister.delete(m) 92 | if err != nil { 93 | return fmt.Errorf("map register: %w", err) 94 | } 95 | 96 | return m.close() 97 | } 98 | -------------------------------------------------------------------------------- /map_prog_array.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/dylandreimerink/gobpfld/bpfsys" 8 | "github.com/dylandreimerink/gobpfld/bpftypes" 9 | ) 10 | 11 | var _ BPFMap = (*ProgArrayMap)(nil) 12 | 13 | // ProgArrayMap is a specialized map type used for tail calls https://docs.cilium.io/en/stable/bpf/#tail-calls 14 | type ProgArrayMap struct { 15 | AbstractMap 16 | } 17 | 18 | func (m *ProgArrayMap) Load() error { 19 | if m.Definition.Type != bpftypes.BPF_MAP_TYPE_PROG_ARRAY { 20 | return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_PROG_ARRAY when using an ProgArrayMap") 21 | } 22 | 23 | err := m.load(nil) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | err = mapRegister.add(m) 29 | if err != nil { 30 | return fmt.Errorf("map register: %w", err) 31 | } 32 | 33 | return nil 34 | } 35 | 36 | // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel 37 | // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map. 38 | func (m *ProgArrayMap) Close() error { 39 | err := mapRegister.delete(m) 40 | if err != nil { 41 | return fmt.Errorf("map register: %w", err) 42 | } 43 | 44 | return m.close() 45 | } 46 | 47 | // Get performs a lookup in the xskmap based on the key and returns the file descriptor of the socket 48 | func (m *ProgArrayMap) Get(key int) (int, error) { 49 | if !m.loaded { 50 | return 0, fmt.Errorf("can't read from an unloaded map") 51 | } 52 | 53 | var fd int 54 | attr := &bpfsys.BPFAttrMapElem{ 55 | MapFD: m.fd, 56 | Key: uintptr(unsafe.Pointer(&key)), 57 | Value_NextKey: uintptr(unsafe.Pointer(&fd)), 58 | } 59 | 60 | err := bpfsys.MapLookupElem(attr) 61 | if err != nil { 62 | return 0, fmt.Errorf("bpf syscall error: %w", err) 63 | } 64 | 65 | return fd, nil 66 | } 67 | 68 | func (m *ProgArrayMap) Set(key int32, value BPFProgram) error { 69 | if !m.loaded { 70 | return fmt.Errorf("can't write to an unloaded map") 71 | } 72 | 73 | fd, err := value.Fd() 74 | if err != nil { 75 | return fmt.Errorf("prog fd: %w", err) 76 | } 77 | 78 | attr := &bpfsys.BPFAttrMapElem{ 79 | MapFD: m.fd, 80 | Key: uintptr(unsafe.Pointer(&key)), 81 | Value_NextKey: uintptr(unsafe.Pointer(&fd)), 82 | } 83 | 84 | err = bpfsys.MapUpdateElem(attr) 85 | if err != nil { 86 | return fmt.Errorf("bpf syscall error: %w", err) 87 | } 88 | 89 | return nil 90 | } 91 | 92 | // TODO add remaining map functions like Delete and iterator 93 | -------------------------------------------------------------------------------- /map_queue.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/bpfsys" 7 | "github.com/dylandreimerink/gobpfld/bpftypes" 8 | ) 9 | 10 | var _ BPFMap = (*QueueMap)(nil) 11 | 12 | // QueueMap is a specialized map type, it has no key type, only a value type. It works like any other FIFO queue. 13 | type QueueMap struct { 14 | AbstractMap 15 | } 16 | 17 | func (m *QueueMap) Load() error { 18 | if m.Definition.Type != bpftypes.BPF_MAP_TYPE_QUEUE { 19 | return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_QUEUE when using an QueueMap") 20 | } 21 | 22 | err := m.load(nil) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | err = mapRegister.add(m) 28 | if err != nil { 29 | return fmt.Errorf("map register: %w", err) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel 36 | // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map. 37 | func (m *QueueMap) Close() error { 38 | err := mapRegister.delete(m) 39 | if err != nil { 40 | return fmt.Errorf("map register: %w", err) 41 | } 42 | 43 | return m.close() 44 | } 45 | 46 | // Enqueue enqueues a new value 47 | func (m *QueueMap) Enqueue(value interface{}) error { 48 | return m.set(nil, value, bpfsys.BPFMapElemAny) 49 | } 50 | 51 | // Peek peeks at the first value in the queue without removing it. 52 | func (m *QueueMap) Peek(value interface{}) error { 53 | return m.get(nil, value) 54 | } 55 | 56 | // Dequeue returns the value of the from of the queue, removing it in the process. 57 | func (m *QueueMap) Dequeue(value interface{}) error { 58 | return m.lookupAndDelete(nil, value, bpfsys.BPFMapElemAny) 59 | } 60 | 61 | // Iterator returns a map iterator which can be used to loop over all values of the map. 62 | // Looping over the queue will consume all values. 63 | func (m *QueueMap) Iterator() MapIterator { 64 | return &lookupAndDeleteIterator{ 65 | BPFMap: m, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /map_queue_test.go: -------------------------------------------------------------------------------- 1 | //go:build bpftests 2 | // +build bpftests 3 | 4 | package gobpfld 5 | 6 | import ( 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/dylandreimerink/gobpfld/bpftypes" 11 | "github.com/dylandreimerink/gobpfld/kernelsupport" 12 | ) 13 | 14 | func TestQueueMap(t *testing.T) { 15 | if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapQueue) { 16 | t.Skip("skipping test, current kernel version has no support for queue maps") 17 | } 18 | 19 | const maxEntries = 20 20 | queue := QueueMap{ 21 | AbstractMap: AbstractMap{ 22 | Name: MustNewObjName("test"), 23 | Definition: BPFMapDef{ 24 | Type: bpftypes.BPF_MAP_TYPE_QUEUE, 25 | KeySize: 0, 26 | ValueSize: sizeOfUint64, 27 | MaxEntries: maxEntries, 28 | }, 29 | }, 30 | } 31 | 32 | err := queue.Load() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | defer queue.Close() 37 | 38 | validationMap := make([]int64, maxEntries) 39 | for i := 0; i < maxEntries; i++ { 40 | val := rand.Int63() 41 | err = queue.Enqueue(&val) 42 | validationMap[i] = int64(val) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | } 47 | 48 | var v int64 49 | err = queue.Peek(&v) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | if v != validationMap[0] { 55 | t.Fatalf("invalid peek, got: %d, expected: %d", v, validationMap[0]) 56 | } 57 | 58 | for i := 0; i >= maxEntries; i-- { 59 | var v int64 60 | err = queue.Dequeue(&v) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | 65 | if v != validationMap[i] { 66 | t.Fatalf("invalid dequeue, got: %d, expected: %d", v, validationMap[i]) 67 | } 68 | } 69 | } 70 | 71 | func TestQueueMapIterator(t *testing.T) { 72 | if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapQueue) { 73 | t.Skip("skipping test, current kernel version has no support for queue maps") 74 | } 75 | 76 | const maxEntries = 20 77 | queue := QueueMap{ 78 | AbstractMap: AbstractMap{ 79 | Name: MustNewObjName("test"), 80 | Definition: BPFMapDef{ 81 | Type: bpftypes.BPF_MAP_TYPE_QUEUE, 82 | KeySize: 0, 83 | ValueSize: sizeOfUint64, 84 | MaxEntries: maxEntries, 85 | }, 86 | }, 87 | } 88 | 89 | err := queue.Load() 90 | if err != nil { 91 | t.Fatal(err) 92 | } 93 | defer queue.Close() 94 | 95 | validationMap := make([]int64, maxEntries) 96 | for i := 0; i < maxEntries; i++ { 97 | val := rand.Int63() 98 | err = queue.Enqueue(&val) 99 | validationMap[i] = int64(val) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | } 104 | 105 | i := 0 106 | var v int64 107 | err = MapIterForEach(queue.Iterator(), nil, &v, func(_, _ interface{}) error { 108 | if v != validationMap[i] { 109 | t.Fatalf("invalid dequeue, got: %d, expected: %d", v, validationMap[i]) 110 | } 111 | i++ 112 | 113 | return nil 114 | }) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /map_stack.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/bpfsys" 7 | "github.com/dylandreimerink/gobpfld/bpftypes" 8 | ) 9 | 10 | var _ BPFMap = (*StackMap)(nil) 11 | 12 | // StackMap is a specialized map type, it has no key type, only a value type. It works like any other FILO stack. 13 | type StackMap struct { 14 | AbstractMap 15 | } 16 | 17 | func (m *StackMap) Load() error { 18 | if m.Definition.Type != bpftypes.BPF_MAP_TYPE_STACK { 19 | return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_STACK when using an StackMap") 20 | } 21 | 22 | err := m.load(nil) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | err = mapRegister.add(m) 28 | if err != nil { 29 | return fmt.Errorf("map register: %w", err) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel 36 | // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map. 37 | func (m *StackMap) Close() error { 38 | err := mapRegister.delete(m) 39 | if err != nil { 40 | return fmt.Errorf("map register: %w", err) 41 | } 42 | 43 | return m.close() 44 | } 45 | 46 | // Push pushes a new value onto the stack 47 | func (m *StackMap) Push(value interface{}) error { 48 | return m.set(nil, value, bpfsys.BPFMapElemAny) 49 | } 50 | 51 | // Peek peeks at the value at the top of the stack without removing it. 52 | func (m *StackMap) Peek(value interface{}) error { 53 | return m.get(nil, value) 54 | } 55 | 56 | // Pop returns the top value of the stack, removing it in the process. 57 | func (m *StackMap) Pop(value interface{}) error { 58 | return m.lookupAndDelete(nil, value, bpfsys.BPFMapElemAny) 59 | } 60 | 61 | // Iterator returns a map iterator which can be used to loop over all values of the map. 62 | // Looping over the stack will consume all values. 63 | func (m *StackMap) Iterator() MapIterator { 64 | return &lookupAndDeleteIterator{ 65 | BPFMap: m, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /map_stack_test.go: -------------------------------------------------------------------------------- 1 | //go:build bpftests 2 | // +build bpftests 3 | 4 | package gobpfld 5 | 6 | import ( 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/dylandreimerink/gobpfld/bpftypes" 11 | "github.com/dylandreimerink/gobpfld/kernelsupport" 12 | ) 13 | 14 | func TestStackMap(t *testing.T) { 15 | if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapStack) { 16 | t.Skip("skipping test, current kernel version has no support for stack maps") 17 | } 18 | 19 | const maxEntries = 20 20 | stack := StackMap{ 21 | AbstractMap: AbstractMap{ 22 | Name: MustNewObjName("test"), 23 | Definition: BPFMapDef{ 24 | Type: bpftypes.BPF_MAP_TYPE_STACK, 25 | KeySize: 0, 26 | ValueSize: sizeOfUint64, 27 | MaxEntries: maxEntries, 28 | }, 29 | }, 30 | } 31 | 32 | err := stack.Load() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | defer stack.Close() 37 | 38 | validationMap := make([]int64, maxEntries) 39 | for i := 0; i < maxEntries; i++ { 40 | val := rand.Int63() 41 | err = stack.Push(&val) 42 | validationMap[i] = int64(val) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | } 47 | 48 | var v int64 49 | err = stack.Peek(&v) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | if v != validationMap[len(validationMap)-1] { 55 | t.Fatalf("invalid peek, got: %d, expected: %d", v, validationMap[len(validationMap)-1]) 56 | } 57 | 58 | for i := maxEntries - 1; i >= 0; i-- { 59 | var v int64 60 | err = stack.Pop(&v) 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | 65 | if v != validationMap[i] { 66 | t.Fatalf("invalid pop, got: %d, expected: %d", v, validationMap[i]) 67 | } 68 | } 69 | } 70 | 71 | func TestStackMapIterator(t *testing.T) { 72 | if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapStack) { 73 | t.Skip("skipping test, current kernel version has no support for stack maps") 74 | } 75 | 76 | const maxEntries = 20 77 | stack := StackMap{ 78 | AbstractMap: AbstractMap{ 79 | Name: MustNewObjName("test"), 80 | Definition: BPFMapDef{ 81 | Type: bpftypes.BPF_MAP_TYPE_STACK, 82 | KeySize: 0, 83 | ValueSize: sizeOfUint64, 84 | MaxEntries: maxEntries, 85 | }, 86 | }, 87 | } 88 | 89 | err := stack.Load() 90 | if err != nil { 91 | t.Fatal(err) 92 | } 93 | defer stack.Close() 94 | 95 | validationMap := make([]int64, maxEntries) 96 | for i := 0; i < maxEntries; i++ { 97 | val := rand.Int63() 98 | err = stack.Push(&val) 99 | validationMap[i] = int64(val) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | } 104 | 105 | i := maxEntries - 1 106 | var v int64 107 | err = MapIterForEach(stack.Iterator(), nil, &v, func(_, _ interface{}) error { 108 | if v != validationMap[i] { 109 | t.Fatalf("invalid pop, got: %d, expected: %d", v, validationMap[i]) 110 | } 111 | i-- 112 | 113 | return nil 114 | }) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /mmap.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import _ "unsafe" // Need to import unsafe to allow the go:linkname directive 4 | 5 | // mmap is a link to the unexposed syscall.mmap which allows us to call the mmap function without restrictions. 6 | // This is nessessery to allow us to specify our own addr, which we can't via the exported syscall.Mmap function. 7 | //go:linkname mmap syscall.mmap 8 | func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) 9 | -------------------------------------------------------------------------------- /perf/doc.go: -------------------------------------------------------------------------------- 1 | // Package perf contains logic to interact with the linux perf subsystem. The open_perf_event syscall ABI and debugfs 2 | // logic is large enough to warrant its own package so as to not clutter up the gobpfld package. The functionality in 3 | // this package will be used to attach kprobe, tracepoint, perf event, raw tracepoint and trancing programs. 4 | package perf 5 | -------------------------------------------------------------------------------- /program_tracepoint.go: -------------------------------------------------------------------------------- 1 | package gobpfld 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dylandreimerink/gobpfld/bpfsys" 7 | "github.com/dylandreimerink/gobpfld/bpftypes" 8 | "github.com/dylandreimerink/gobpfld/perf" 9 | ) 10 | 11 | var _ BPFProgram = (*ProgramTracepoint)(nil) 12 | 13 | type ProgramTracepoint struct { 14 | AbstractBPFProgram 15 | 16 | // DefaultCategory is the tracepoint category used if no category is specified during attaching. 17 | // It can be set when loading from ELF file. 18 | DefaultCategory string 19 | // DefaultName is the tracepoint name used if no name is specified during attaching. 20 | // It can be set when loading from ELF file. 21 | DefaultName string 22 | 23 | attachedEvent *perf.Event 24 | } 25 | 26 | type ProgTPLoadOpts struct { 27 | VerifierLogLevel bpftypes.BPFLogLevel 28 | VerifierLogSize int 29 | } 30 | 31 | func (p *ProgramTracepoint) Load(opts ProgTPLoadOpts) (log string, err error) { 32 | return p.load(bpfsys.BPFAttrProgramLoad{ 33 | LogLevel: opts.VerifierLogLevel, 34 | LogSize: uint32(opts.VerifierLogSize), 35 | }) 36 | } 37 | 38 | // Unpin captures the file descriptor of the program at the given 'relativePath' from the kernel. 39 | // If 'deletePin' is true the bpf FS pin will be removed after successfully loading the program, thus transferring 40 | // ownership of the program in a scenario where the program is not shared between multiple userspace programs. 41 | // Otherwise the pin will keep existing which will cause the map to not be deleted when this program exits. 42 | func (p *ProgramTracepoint) Unpin(relativePath string, deletePin bool) error { 43 | return p.unpin(relativePath, deletePin) 44 | } 45 | 46 | type ProgTPAttachOpts struct { 47 | Category string 48 | Name string 49 | } 50 | 51 | func (p *ProgramTracepoint) Attach(opts ProgTPAttachOpts) error { 52 | category := p.DefaultCategory 53 | if opts.Category != "" { 54 | category = opts.Category 55 | } 56 | 57 | name := p.DefaultName 58 | if opts.Category != "" { 59 | name = opts.Name 60 | } 61 | 62 | var err error 63 | p.attachedEvent, err = perf.OpenTracepointEvent(category, name) 64 | if err != nil { 65 | return fmt.Errorf("open tracepoint: %w", err) 66 | } 67 | 68 | err = p.attachedEvent.AttachBPFProgram(p.fd) 69 | if err != nil { 70 | return fmt.Errorf("attach program: %w", err) 71 | } 72 | 73 | return nil 74 | } 75 | 76 | func (p *ProgramTracepoint) Detach() error { 77 | return p.attachedEvent.DetachBPFProgram() 78 | } 79 | -------------------------------------------------------------------------------- /tools/check-go-mod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | go mod tidy 4 | STATUS=$( git status --porcelain go.mod go.sum ) 5 | if [ ! -z "$STATUS" ]; then 6 | echo "Running go mod tidy modified go.mod and/or go.sum" 7 | exit 1 8 | fi 9 | exit 0 --------------------------------------------------------------------------------