├── Makefile ├── examples ├── Makefile ├── test_ft.h ├── test_bpf.go ├── test_bpf.c ├── test_ft.go └── test_ft.c ├── README.md ├── libbpf.h ├── LICENSE └── goebpf.go /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: examples 2 | 3 | examples: 4 | $(MAKE) -C examples 5 | 6 | clean: 7 | $(MAKE) -C examples clean 8 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | LLC ?= llc 2 | CLANG ?= clang 3 | 4 | all: test_pbf test_ft 5 | 6 | test_pbf: ../goebpf.go test_bpf.go test_bpf.o 7 | go build ./test_bpf.go 8 | 9 | test_ft: ../goebpf.go test_ft.go test_ft.o 10 | go build ./test_ft.go 11 | 12 | %.o: %.c 13 | $(CLANG) \ 14 | -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ 15 | -Wno-compare-distinct-pointer-types \ 16 | -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ 17 | 18 | clean: 19 | rm -f *.o test_pbf test_ft 20 | -------------------------------------------------------------------------------- /examples/test_ft.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_FT_H 2 | #define __TEST_FT_H 3 | 4 | #include 5 | #include 6 | 7 | struct layer_stats { 8 | __u64 packets; 9 | __u64 bytes; 10 | }; 11 | 12 | struct link_layer { 13 | unsigned char mac_src[ETH_ALEN]; 14 | unsigned char mac_dst[ETH_ALEN]; 15 | }; 16 | 17 | struct network_layer { 18 | __be32 ip_src; 19 | __be32 ip_dst; 20 | }; 21 | 22 | struct transport_layer { 23 | __u8 protocol; 24 | __be16 port_src; 25 | __be16 port_dst; 26 | }; 27 | 28 | struct flow_key { 29 | struct link_layer link_layer; 30 | struct network_layer network_layer; 31 | struct transport_layer transport_layer; 32 | }; 33 | 34 | struct flow_stats { 35 | struct layer_stats link_layer; 36 | struct layer_stats network_layer; 37 | struct layer_stats transport_layer; 38 | }; 39 | 40 | struct flow { 41 | struct flow_key key; 42 | struct flow_stats stats; 43 | 44 | __u64 start; 45 | __u64 last; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /examples/test_bpf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/safchain/goebpf" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open("test_bpf.o") 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | b, err := goebpf.NewBPFProg(f) 18 | if err != nil { 19 | panic(err) 20 | } 21 | defer b.Release() 22 | 23 | b.SetDefaultMaxEntries(300) 24 | 25 | err = b.Load() 26 | if err != nil { 27 | fmt.Println(string(b.Log())) 28 | panic(err) 29 | } 30 | 31 | fmt.Println(string(b.Log())) 32 | 33 | fmt.Println(b.Maps()) 34 | 35 | fd, err := b.Attach("wlp4s0") 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | for i := 0; i != 50; i++ { 41 | value := make([]byte, 300) 42 | 43 | key := uint32(44) 44 | 45 | b.Map("my_map1").Lookup(&key, value) 46 | fmt.Printf("First map value: %v\n", value) 47 | 48 | 49 | b.Map("my_map2").Lookup([]byte("abc"), value) 50 | fmt.Printf("Second map value: %v\n", value) 51 | 52 | time.Sleep(1 * time.Second) 53 | } 54 | 55 | err = b.Detach(fd) 56 | if err != nil { 57 | panic(err) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/test_bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../libbpf.h" 11 | 12 | MAP(my_map1) { 13 | .type = BPF_MAP_TYPE_ARRAY, 14 | .key_size = sizeof(uint32_t), 15 | .value_size = sizeof(long), 16 | .max_entries = 256, 17 | }; 18 | 19 | MAP(my_map2) { 20 | .type = BPF_MAP_TYPE_HASH, 21 | .key_size = 4, 22 | .value_size = sizeof(long), 23 | .max_entries = 256, 24 | }; 25 | 26 | SOCKET(test) 27 | int bpf_test(struct __sk_buff *skb) 28 | { 29 | uint32_t index = 44; 30 | char key[4] = "abc"; 31 | long *value1, *value2; 32 | 33 | if (skb->pkt_type != PACKET_OUTGOING) 34 | return 0; 35 | 36 | value1 = bpf_map_lookup_element(&my_map1, &index); 37 | if (value1 != NULL && *value1 == 0) { 38 | long v = 778; 39 | bpf_map_update_element(&my_map1, &index, &v, BPF_ANY); 40 | } 41 | 42 | value2 = bpf_map_lookup_element(&my_map2, key); 43 | if (value2) 44 | __sync_fetch_and_add(value2, 10000); 45 | else { 46 | long v = 999; 47 | bpf_map_update_element(&my_map2, key, &v, BPF_ANY); 48 | } 49 | 50 | return 0; 51 | } 52 | char _license[] LICENSE = "GPL"; 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goebpf - eBPF library in Go 2 | 3 | This package provides a very simple library for handling eBPF programs. It 4 | provides a header file that have to be used to compile eBPF elf binary. 5 | 6 | ## Dependencies 7 | 8 | There is no dependency to use the Go library but there are dependencies to build 9 | eBPF elf binaries. In order to build an eBPF program this package provides the 10 | header file libbpf.h. LLVM/Clang with the support of bpf as target must 11 | be available. 12 | 13 | * clang >= version 3.4.0 14 | * llvm >= version 3.7.0 15 | 16 | ## Install 17 | 18 | You can use go get command to retrieve the package: 19 | 20 | ```console 21 | go get github.com/safchain/goebpf 22 | ``` 23 | 24 | ## Examples 25 | 26 | In the examples folder there is a test file in Go and there is also a eBPF 27 | C file that will be loaded by the Go file once compiled. The Makefile in the 28 | examples folder will show to compile eBPF program. 29 | 30 | In order to simply build the examples : 31 | 32 | ``` 33 | make examples 34 | ``` 35 | 36 | ## Documentation 37 | 38 | Further informations can be found here : 39 | 40 | https://github.com/torvalds/linux/tree/master/samples/bpf 41 | 42 | 43 | ## License 44 | This software is licensed under the Apache License, Version 2.0 (the 45 | "License"); you may not use this software except in compliance with the 46 | License. 47 | You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 48 | 49 | Unless required by applicable law or agreed to in writing, software 50 | distributed under the License is distributed on an "AS IS" BASIS, 51 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 52 | See the License for the specific language governing permissions and 53 | limitations under the License. 54 | -------------------------------------------------------------------------------- /libbpf.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBBPF_H 2 | #define __LIBBPF_H 3 | 4 | /* bpf map structure used by C program to define maps and 5 | * used by elf loader. 6 | */ 7 | typedef struct bpf_map_def { 8 | unsigned int type; 9 | unsigned int key_size; 10 | unsigned int value_size; 11 | unsigned int max_entries; 12 | } bpf_map_def; 13 | 14 | /* helper macro to place different sections in eBPF elf file. This is a generic 15 | * macro, more specific macro should be used instead of this one. 16 | */ 17 | #define SEC(NAME) __attribute__((section(NAME), used)) 18 | 19 | /* helper marcro to define a map, socket, kprobe section in the 20 | * eBPF elf file. 21 | */ 22 | #define MAP(NAME) struct bpf_map_def __attribute__((section("maps/"#NAME), used)) NAME = 23 | #define SOCKET(NAME) __attribute__((section("sockets/"#NAME), used)) 24 | #define LICENSE SEC("license") 25 | 26 | /* llvm built-in functions */ 27 | unsigned long long load_byte(void *skb, 28 | unsigned long long off) asm("llvm.bpf.load.byte"); 29 | unsigned long long load_half(void *skb, 30 | unsigned long long off) asm("llvm.bpf.load.half"); 31 | unsigned long long load_word(void *skb, 32 | unsigned long long off) asm("llvm.bpf.load.word"); 33 | 34 | /* helper functions called from eBPF programs written in C 35 | */ 36 | static void *(*bpf_map_lookup_element)(void *map, void *key) = 37 | (void *) BPF_FUNC_map_lookup_elem; 38 | static int (*bpf_map_update_element)(void *map, void *key, void *value, 39 | unsigned long long flags) = (void *) BPF_FUNC_map_update_elem; 40 | static int (*bpf_map_delete_element)(void *map, void *key) = 41 | (void *) BPF_FUNC_map_delete_elem; 42 | static unsigned long long (*bpf_ktime_get_ns)(void) = 43 | (void *) BPF_FUNC_ktime_get_ns; 44 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 45 | (void *) BPF_FUNC_trace_printk; 46 | static void (*bpf_tail_call)(void *ctx, void *map, int index) = 47 | (void *) BPF_FUNC_tail_call; 48 | static unsigned long long (*bpf_get_smp_processor_id)(void) = 49 | (void *) BPF_FUNC_get_smp_processor_id; 50 | static unsigned long long (*bpf_get_current_pid_tgid)(void) = 51 | (void *) BPF_FUNC_get_current_pid_tgid; 52 | static unsigned long long (*bpf_get_current_uid_gid)(void) = 53 | (void *) BPF_FUNC_get_current_uid_gid; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /examples/test_ft.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "syscall" 8 | "time" 9 | "unsafe" 10 | 11 | "github.com/safchain/goebpf" 12 | ) 13 | 14 | // #include "test_ft.h" 15 | import "C" 16 | 17 | func uint32ToIPV4(i uint32) net.IP { 18 | return net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)) 19 | } 20 | 21 | func main() { 22 | f, err := os.Open("test_ft.o") 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | b, err := goebpf.NewBPFProg(f) 28 | if err != nil { 29 | panic(err) 30 | } 31 | defer b.Release() 32 | 33 | b.SetDefaultMaxEntries(300) 34 | 35 | err = b.Load() 36 | if err != nil { 37 | fmt.Println(string(b.Log())) 38 | panic(err) 39 | } 40 | 41 | fmt.Println(string(b.Log())) 42 | 43 | fmt.Println(b.Maps()) 44 | 45 | _, err = b.Attach("wlp4s0") 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | var start int64 51 | flow := C.struct_flow{} 52 | 53 | for { 54 | var info syscall.Sysinfo_t 55 | syscall.Sysinfo(&info) 56 | 57 | count := 0 58 | 59 | it := b.Map("flow_table").Iterator() 60 | for it.Next(&flow.key, &flow) { 61 | linkSrc := C.GoBytes(unsafe.Pointer(&flow.key.link_layer.mac_src[0]), C.ETH_ALEN) 62 | linkDst := C.GoBytes(unsafe.Pointer(&flow.key.link_layer.mac_dst[0]), C.ETH_ALEN) 63 | 64 | macSrc := net.HardwareAddr(linkSrc).String() 65 | macDst := net.HardwareAddr(linkDst).String() 66 | 67 | packets := uint64(flow.stats.link_layer.packets) 68 | bytes := uint64(flow.stats.link_layer.bytes) 69 | 70 | fmt.Printf("%s ==> %s : %d packets, %d bytes\n", macSrc, macDst, packets, bytes) 71 | 72 | ipSrc := uint32(flow.key.network_layer.ip_src) 73 | ipDst := uint32(flow.key.network_layer.ip_dst) 74 | 75 | packets = uint64(flow.stats.network_layer.packets) 76 | bytes = uint64(flow.stats.network_layer.bytes) 77 | 78 | fmt.Printf("\t%s ==> %s : %d packets, %d bytes\n", 79 | uint32ToIPV4(ipSrc).String(), uint32ToIPV4(ipDst).String(), packets, bytes) 80 | 81 | protocol := uint8(flow.key.transport_layer.protocol) 82 | portSrc := uint32(flow.key.transport_layer.port_src) 83 | portDst := uint32(flow.key.transport_layer.port_dst) 84 | 85 | packets = uint64(flow.stats.transport_layer.packets) 86 | bytes = uint64(flow.stats.transport_layer.bytes) 87 | 88 | fmt.Printf("\t%d: %d ==> %d : %d packets, %d bytes\n", 89 | protocol, portSrc, portDst, packets, bytes) 90 | 91 | if start == 0 { 92 | start = int64(flow.start) 93 | } 94 | 95 | last := int64(flow.last) 96 | if last-start > int64(10*time.Second) { 97 | b.Map("flow_table").Delete(&flow.key) 98 | } 99 | 100 | count++ 101 | } 102 | fmt.Printf("-------------------- Total: %d -------------------\n", count) 103 | time.Sleep(1 * time.Second) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/test_ft.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../libbpf.h" 12 | #include "test_ft.h" 13 | 14 | MAP(flow_table) { 15 | .type = BPF_MAP_TYPE_HASH, 16 | .key_size = sizeof(struct flow_key), 17 | .value_size = sizeof(struct flow), 18 | .max_entries = 256, 19 | }; 20 | 21 | static void fill_transport(struct __sk_buff *skb, __u8 protocol, int offset, 22 | struct flow_key *key) 23 | { 24 | struct transport_layer *layer = &key->transport_layer; 25 | 26 | layer->protocol = protocol; 27 | layer->port_src = load_half(skb, offset); 28 | layer->port_dst = load_half(skb, offset + sizeof(__be16)); 29 | } 30 | 31 | static void update_transport_stats(struct __sk_buff *skb, int offset, 32 | struct flow_stats *stats) 33 | { 34 | __sync_fetch_and_add(&stats->transport_layer.packets, 1); 35 | __sync_fetch_and_add(&stats->transport_layer.bytes, skb->len - offset); 36 | } 37 | 38 | static void fill_network(struct __sk_buff *skb, int offset, 39 | struct flow_key *key) 40 | { 41 | struct network_layer *layer = &key->network_layer; 42 | 43 | layer->ip_src = load_word(skb, offset + offsetof(struct iphdr, saddr)); 44 | layer->ip_dst = load_word(skb, offset + offsetof(struct iphdr, daddr)); 45 | 46 | __u8 protocol = load_byte(skb, offset + offsetof(struct iphdr, protocol)); 47 | 48 | __u8 verlen = load_byte(skb, offset); 49 | offset += (verlen & 0xF) << 2; 50 | 51 | switch (protocol) { 52 | case IPPROTO_TCP: 53 | case IPPROTO_UDP: 54 | case IPPROTO_SCTP: 55 | fill_transport(skb, protocol, offset, key); 56 | } 57 | } 58 | 59 | static void update_network_stats(struct __sk_buff *skb, int offset, 60 | struct flow_stats *stats) 61 | { 62 | __sync_fetch_and_add(&stats->network_layer.packets, 1); 63 | __sync_fetch_and_add(&stats->network_layer.bytes, skb->len - offset); 64 | 65 | __u32 proto = load_byte(skb, offset + offsetof(struct iphdr, protocol)); 66 | 67 | __u8 verlen = load_byte(skb, offset); 68 | offset += (verlen & 0xF) << 2; 69 | 70 | switch (proto) { 71 | case IPPROTO_TCP: 72 | case IPPROTO_UDP: 73 | case IPPROTO_SCTP: 74 | update_transport_stats(skb, offset, stats); 75 | } 76 | } 77 | 78 | static __always_inline void _fill_haddr(struct __sk_buff *skb, int offset, 79 | unsigned char *mac) 80 | { 81 | mac[0] = load_byte(skb, offset); 82 | mac[1] = load_byte(skb, offset + 1); 83 | mac[2] = load_byte(skb, offset + 2); 84 | mac[3] = load_byte(skb, offset + 3); 85 | mac[4] = load_byte(skb, offset + 4); 86 | mac[5] = load_byte(skb, offset + 5); 87 | } 88 | 89 | static void fill_link(struct __sk_buff *skb, int offset, struct flow_key *key) 90 | { 91 | struct link_layer *layer = &key->link_layer; 92 | 93 | _fill_haddr(skb, offset + offsetof(struct ethhdr, h_source), layer->mac_src); 94 | _fill_haddr(skb, offset + offsetof(struct ethhdr, h_dest), layer->mac_dst); 95 | } 96 | 97 | static void update_link_stats(struct __sk_buff *skb, int offset, 98 | struct flow_stats *stats) 99 | { 100 | __sync_fetch_and_add(&stats->link_layer.packets, 1); 101 | __sync_fetch_and_add(&stats->link_layer.bytes, skb->len); 102 | } 103 | 104 | static void update_stats(struct __sk_buff *skb, struct flow_stats *stats) 105 | { 106 | update_link_stats(skb, 0, stats); 107 | 108 | __u32 proto = load_half(skb, offsetof(struct ethhdr, h_proto)); 109 | switch (proto) { 110 | case ETH_P_IP: 111 | update_network_stats(skb, ETH_HLEN, stats); 112 | } 113 | } 114 | 115 | static void fill_key(struct __sk_buff *skb, struct flow_key *key) 116 | { 117 | fill_link(skb, 0, key); 118 | 119 | __u32 proto = load_half(skb, offsetof(struct ethhdr, h_proto)); 120 | switch (proto) { 121 | case ETH_P_IP: 122 | fill_network(skb, ETH_HLEN, key); 123 | } 124 | } 125 | 126 | SOCKET(test) 127 | int bpf_test(struct __sk_buff *skb) 128 | { 129 | if (skb->pkt_type != PACKET_OUTGOING) 130 | return 0; 131 | 132 | __u64 tm = bpf_ktime_get_ns(); 133 | 134 | struct flow flow = {}, *prev; 135 | fill_key(skb, &flow.key); 136 | 137 | prev = bpf_map_lookup_element(&flow_table, &flow.key); 138 | if (prev) { 139 | update_stats(skb, &prev->stats); 140 | prev->last = tm; 141 | } else { 142 | update_stats(skb, &flow.stats); 143 | flow.start = tm; 144 | flow.last = tm; 145 | 146 | bpf_map_update_element(&flow_table, &flow.key, &flow, BPF_ANY); 147 | } 148 | 149 | return 0; 150 | } 151 | char _license[] LICENSE = "GPL"; 152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /goebpf.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | */ 21 | 22 | // Package goebpf provides a simple library for handling eBPF programs. The eBPF 23 | // programs have to be compiled with the header file coming with the package. 24 | // This package provides a way to load the generated elf binaries and to do 25 | // lookups on the eBPF maps. 26 | package goebpf 27 | 28 | /* 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "libbpf.h" 44 | 45 | typedef struct bpf_map { 46 | int fd; 47 | bpf_map_def def; 48 | } bpf_map; 49 | 50 | static __u64 time_get_ns(void) 51 | { 52 | struct timespec ts; 53 | 54 | clock_gettime(CLOCK_MONOTONIC, &ts); 55 | return ts.tv_sec * 1000000000ull + ts.tv_nsec; 56 | } 57 | 58 | static __u64 ptr_to_u64(void *ptr) 59 | { 60 | return (__u64) (unsigned long) ptr; 61 | } 62 | 63 | static void bpf_apply_relocation(int fd, struct bpf_insn *insn) 64 | { 65 | insn->src_reg = BPF_PSEUDO_MAP_FD; 66 | insn->imm = fd; 67 | } 68 | 69 | static int bpf_create_map(enum bpf_map_type map_type, int key_size, 70 | int value_size, int max_entries) 71 | { 72 | union bpf_attr attr; 73 | memset(&attr, 0, sizeof(attr)); 74 | 75 | attr.map_type = map_type; 76 | attr.key_size = key_size; 77 | attr.value_size = value_size; 78 | attr.max_entries = max_entries; 79 | 80 | return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); 81 | } 82 | 83 | static bpf_map *bpf_load_map(bpf_map_def *map_def, int max) 84 | { 85 | bpf_map *map; 86 | 87 | map = calloc(1, sizeof(bpf_map)); 88 | if (map == NULL) 89 | return NULL; 90 | 91 | memcpy(&map->def, map_def, sizeof(bpf_map_def)); 92 | 93 | if (max == 0) 94 | max = map_def->max_entries; 95 | 96 | map->fd = bpf_create_map(map_def->type, 97 | map_def->key_size, 98 | map_def->value_size, 99 | max 100 | ); 101 | 102 | if (map->fd < 0) 103 | return 0; 104 | 105 | return map; 106 | } 107 | 108 | static int bpf_attach(int prog_fd, int fd) 109 | { 110 | return setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF, &(prog_fd), 111 | sizeof(prog_fd)) == 0; 112 | } 113 | 114 | static int bpf_detach(int prog_fd, int fd) 115 | { 116 | return setsockopt(fd, SOL_SOCKET, SO_DETACH_BPF, &(prog_fd), 117 | sizeof(prog_fd)) == 0; 118 | } 119 | 120 | static int bpf_prog_load(enum bpf_prog_type prog_type, 121 | const struct bpf_insn *insns, int prog_len, 122 | const char *license, int kern_version, 123 | char *log_buf, int log_size) 124 | { 125 | union bpf_attr attr; 126 | memset(&attr, 0, sizeof(attr)); 127 | 128 | attr.prog_type = prog_type; 129 | attr.insn_cnt = prog_len / sizeof(struct bpf_insn); 130 | attr.insns = ptr_to_u64((void *) insns); 131 | attr.license = ptr_to_u64((void *) license); 132 | attr.log_buf = ptr_to_u64(log_buf); 133 | attr.log_size = log_size; 134 | attr.log_level = 1; 135 | attr.kern_version = kern_version; 136 | 137 | return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); 138 | } 139 | 140 | static int bpf_delete_element(int fd, void *key) 141 | { 142 | union bpf_attr attr = { 143 | .map_fd = fd, 144 | .key = ptr_to_u64(key) 145 | }; 146 | 147 | return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)) == 0; 148 | } 149 | 150 | static int bpf_get_next_key(int fd, void *key, void *next_key) 151 | { 152 | union bpf_attr attr = { 153 | .map_fd = fd, 154 | .key = ptr_to_u64(key), 155 | .next_key = ptr_to_u64(next_key), 156 | }; 157 | 158 | return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)) == 0; 159 | } 160 | 161 | static int bpf_lookup_element(int fd, void *key, void *value) 162 | { 163 | union bpf_attr attr = { 164 | .map_fd = fd, 165 | .key = ptr_to_u64(key), 166 | .value = ptr_to_u64(value), 167 | }; 168 | 169 | return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)) == 0; 170 | } 171 | 172 | static int open_raw_sock(const char *name) 173 | { 174 | struct sockaddr_ll sll; 175 | int fd; 176 | 177 | fd = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, 178 | htons(ETH_P_ALL)); 179 | if (fd < 0) 180 | return 0; 181 | 182 | memset(&sll, 0, sizeof(sll)); 183 | sll.sll_family = AF_PACKET; 184 | sll.sll_ifindex = if_nametoindex(name); 185 | sll.sll_protocol = htons(ETH_P_ALL); 186 | 187 | if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 188 | close(fd); 189 | return 0; 190 | } 191 | 192 | return fd; 193 | } 194 | */ 195 | import "C" 196 | 197 | import ( 198 | "bytes" 199 | "debug/elf" 200 | "encoding/binary" 201 | "errors" 202 | "fmt" 203 | "io" 204 | "reflect" 205 | "strconv" 206 | "strings" 207 | "unsafe" 208 | ) 209 | 210 | // A BPFMapType represents a eBPF map type 211 | type BPFMapType uint32 212 | 213 | // Types of maps 214 | const ( 215 | BPF_MAP_TYPE_UNSPEC BPFMapType = C.BPF_MAP_TYPE_UNSPEC 216 | BPF_MAP_TYPE_HASH 217 | BPF_MAP_TYPE_ARRAY 218 | BPF_MAP_TYPE_PROG_ARRAY 219 | BPF_MAP_TYPE_PERF_EVENT_ARRAY 220 | BPF_MAP_TYPE_PERCPU_HASH 221 | BPF_MAP_TYPE_PERCPU_ARRAY 222 | BPF_MAP_TYPE_STACK_TRACE 223 | ) 224 | 225 | // BPFMap represents a eBPF map. An eBPF map has to be declared in the C file 226 | // using the macro MAP provided by the libbpf.h header file. 227 | type BPFMap struct { 228 | Name string 229 | m *C.bpf_map 230 | } 231 | 232 | type BPFMapIterator struct { 233 | key interface{} 234 | m *BPFMap 235 | } 236 | 237 | // BPFMap represents a eBPF program. 238 | type BPFProg struct { 239 | file *elf.File 240 | fd int 241 | prog_map *BPFMap 242 | maps map[string]*BPFMap 243 | log []byte 244 | verifierLogLevel int 245 | mapsDefaultMaxEntries int 246 | mapsMaxEntries map[string]int 247 | } 248 | 249 | func GetTimeMonotonicNs() uint64 { 250 | return uint64(C.time_get_ns()) 251 | } 252 | 253 | // Release releases the memory allocated by a BPFProg. 254 | func (b *BPFProg) Release() { 255 | for _, m := range b.maps { 256 | C.free(unsafe.Pointer(m.m)) 257 | } 258 | } 259 | 260 | // Log returns the log output coming from the eBPF program. 261 | func (b *BPFProg) Log() []byte { 262 | return b.log 263 | } 264 | 265 | // Type returns the type of a BPFMap wich is the type of the eBPF map. 266 | func (m *BPFMap) Type() BPFMapType { 267 | return BPFMapType(m.m.def._type) 268 | } 269 | 270 | // KeySize returns the key size of a BPFMap/eBPF map. 271 | func (m *BPFMap) KeySize() uint32 { 272 | return uint32(m.m.def.key_size) 273 | } 274 | 275 | // ValueSize returns the value size of a BPFMap/eBPF map. 276 | func (m *BPFMap) ValueSize() uint32 { 277 | return uint32(m.m.def.value_size) 278 | } 279 | 280 | // Lookup does a lookup on the corresponding BPFMap. Key/values parameters 281 | // need to be pointers and need to be be used according to the eBPF map 282 | // definition declared in the eBPF C file. See the libbpf.h file coming with 283 | // this package. 284 | func (m *BPFMap) Lookup(key interface{}, value interface{}) bool { 285 | if m == nil { 286 | return false 287 | } 288 | 289 | k := reflect.ValueOf(key) 290 | v := reflect.ValueOf(value) 291 | 292 | ret := C.bpf_lookup_element(m.m.fd, unsafe.Pointer(k.Pointer()), unsafe.Pointer(v.Pointer())) 293 | if ret == 0 { 294 | return false 295 | } 296 | 297 | return true 298 | } 299 | 300 | // Delete deletes the map entry for the given key. 301 | func (m *BPFMap) Delete(key interface{}) bool { 302 | if m == nil { 303 | return false 304 | } 305 | 306 | k := reflect.ValueOf(key) 307 | 308 | ret := C.bpf_delete_element(m.m.fd, unsafe.Pointer(k.Pointer())) 309 | if ret == 0 { 310 | return false 311 | } 312 | 313 | return true 314 | } 315 | 316 | // Iterator returns a BPFMapIterator 317 | func (m *BPFMap) Iterator() *BPFMapIterator { 318 | return &BPFMapIterator{ 319 | key: make([]byte, m.KeySize()), 320 | m: m, 321 | } 322 | } 323 | 324 | // Next returns the next key, value of the BPFMap, returns true when 325 | // the next element has been found, false otherwise. 326 | func (i *BPFMapIterator) Next(key interface{}, value interface{}) bool { 327 | k := reflect.ValueOf(i.key) 328 | nk := reflect.ValueOf(key) 329 | 330 | ret := C.bpf_get_next_key(i.m.m.fd, unsafe.Pointer(k.Pointer()), unsafe.Pointer(nk.Pointer())) 331 | if ret == 0 { 332 | return false 333 | } 334 | 335 | found := i.m.Lookup(key, value) 336 | if found { 337 | i.key = key 338 | return true 339 | } 340 | 341 | return false 342 | } 343 | 344 | // Attach attaches the eBPF program to the given interface. 345 | func (b *BPFProg) Attach(ifname string) (int, error) { 346 | li := unsafe.Pointer(C.CString(ifname)) 347 | defer C.free(li) 348 | 349 | fd := C.open_raw_sock((*C.char)(li)) 350 | if fd == 0 { 351 | return 0, errors.New("Unable to open raw socket") 352 | } 353 | 354 | ret := C.bpf_attach(C.int(b.fd), fd) 355 | if ret == 0 { 356 | return 0, errors.New("Unable to attach bpf to raw socket") 357 | } 358 | 359 | return int(fd), nil 360 | } 361 | 362 | // Detach detaches the eBPF program to the given interface. 363 | func (b *BPFProg) Detach(fd int) error { 364 | ret := C.bpf_attach(C.int(b.fd), C.int(fd)) 365 | if ret == 0 { 366 | return errors.New("Unable to detach bpf to raw socket") 367 | } 368 | return nil 369 | } 370 | 371 | func (b *BPFProg) relocate(data []byte, rdata []byte) error { 372 | var symbol elf.Symbol 373 | var offset uint64 374 | 375 | symbols, err := b.file.Symbols() 376 | if err != nil { 377 | return err 378 | } 379 | 380 | br := bytes.NewReader(data) 381 | 382 | for { 383 | switch b.file.Class { 384 | case elf.ELFCLASS64: 385 | var rel elf.Rel64 386 | err := binary.Read(br, b.file.ByteOrder, &rel) 387 | if err != nil { 388 | if err == io.EOF { 389 | return nil 390 | } 391 | return err 392 | } 393 | 394 | symNo := rel.Info >> 32 395 | symbol = symbols[symNo-1] 396 | 397 | offset = rel.Off 398 | case elf.ELFCLASS32: 399 | var rel elf.Rel32 400 | err := binary.Read(br, b.file.ByteOrder, &rel) 401 | if err != nil { 402 | if err == io.EOF { 403 | return nil 404 | } 405 | return err 406 | } 407 | 408 | symNo := rel.Info >> 8 409 | symbol = symbols[symNo-1] 410 | 411 | offset = uint64(rel.Off) 412 | default: 413 | return errors.New("Architecture not supported") 414 | } 415 | 416 | rinsn := (*C.struct_bpf_insn)(unsafe.Pointer(&rdata[offset])) 417 | if rinsn.code != (C.BPF_LD | C.BPF_IMM | C.BPF_DW) { 418 | return errors.New("Invalid relocation") 419 | } 420 | 421 | symbolSec := b.file.Sections[symbol.Section] 422 | name := strings.TrimPrefix(symbolSec.Name, "maps/") 423 | 424 | m := b.Map(name) 425 | if m == nil { 426 | return errors.New("Relocation error, map not found") 427 | } 428 | 429 | C.bpf_apply_relocation(m.m.fd, rinsn) 430 | } 431 | } 432 | 433 | func (b *BPFProg) readLicense() (string, error) { 434 | if lsec := b.file.Section("license"); lsec != nil { 435 | data, err := lsec.Data() 436 | if err != nil { 437 | return "", err 438 | } 439 | return string(data), nil 440 | } 441 | 442 | return "", nil 443 | } 444 | 445 | func (b *BPFProg) readVersion() (int64, error) { 446 | if vsec := b.file.Section("version"); vsec != nil { 447 | data, err := vsec.Data() 448 | if err != nil { 449 | return 0, err 450 | } 451 | version, err := strconv.ParseInt(string(data), 10, 64) 452 | if err != nil { 453 | return 0, err 454 | } 455 | return version, nil 456 | } 457 | 458 | return 0, nil 459 | } 460 | 461 | func (b *BPFProg) readMaps() error { 462 | for _, section := range b.file.Sections { 463 | if strings.HasPrefix(section.Name, "maps/") { 464 | data, err := section.Data() 465 | if err != nil { 466 | return err 467 | } 468 | 469 | name := strings.TrimPrefix(section.Name, "maps/") 470 | 471 | maxEntries := b.mapsMaxEntries[name] 472 | if maxEntries == 0 { 473 | maxEntries = b.mapsDefaultMaxEntries 474 | } 475 | 476 | cm := C.bpf_load_map((*C.bpf_map_def)(unsafe.Pointer(&data[0])), C.int(maxEntries)) 477 | if cm == nil { 478 | return fmt.Errorf("Error while loading map %s", section.Name) 479 | } 480 | 481 | m := &BPFMap{Name: name, m: cm} 482 | 483 | if m.Type() == BPF_MAP_TYPE_PROG_ARRAY { 484 | b.prog_map = m 485 | } 486 | 487 | b.maps[name] = m 488 | } 489 | } 490 | 491 | return nil 492 | } 493 | 494 | func (b *BPFProg) load() error { 495 | license, err := b.readLicense() 496 | if err != nil { 497 | return err 498 | } 499 | 500 | lp := unsafe.Pointer(C.CString(license)) 501 | defer C.free(lp) 502 | 503 | version, err := b.readVersion() 504 | if err != nil { 505 | return err 506 | } 507 | 508 | err = b.readMaps() 509 | if err != nil { 510 | return err 511 | } 512 | 513 | processed := make([]bool, len(b.file.Sections)) 514 | for i, section := range b.file.Sections { 515 | if processed[i] { 516 | continue 517 | } 518 | 519 | data, err := section.Data() 520 | if err != nil { 521 | return err 522 | } 523 | 524 | if len(data) == 0 { 525 | continue 526 | } 527 | 528 | if section.Type == elf.SHT_REL { 529 | rsection := b.file.Sections[section.Info] 530 | 531 | processed[i] = true 532 | processed[section.Info] = true 533 | 534 | if strings.HasPrefix(rsection.Name, "sockets/") { 535 | rdata, err := rsection.Data() 536 | if err != nil { 537 | return err 538 | } 539 | 540 | if len(rdata) == 0 { 541 | continue 542 | } 543 | 544 | err = b.relocate(data, rdata) 545 | if err != nil { 546 | return err 547 | } 548 | 549 | insns := (*C.struct_bpf_insn)(unsafe.Pointer(&rdata[0])) 550 | 551 | fd := C.bpf_prog_load(C.BPF_PROG_TYPE_SOCKET_FILTER, 552 | insns, C.int(rsection.Size), 553 | (*C.char)(lp), C.int(version), 554 | (*C.char)(unsafe.Pointer(&b.log[0])), C.int(len(b.log))) 555 | if fd < 0 { 556 | return errors.New("Error while loading") 557 | } 558 | b.fd = int(fd) 559 | } 560 | } 561 | } 562 | 563 | for i, section := range b.file.Sections { 564 | if processed[i] { 565 | continue 566 | } 567 | 568 | if strings.HasPrefix(section.Name, "sockets/") { 569 | data, err := section.Data() 570 | if err != nil { 571 | panic(err) 572 | } 573 | 574 | if len(data) == 0 { 575 | continue 576 | } 577 | 578 | insns := (*C.struct_bpf_insn)(unsafe.Pointer(&data[0])) 579 | 580 | fd := C.bpf_prog_load(C.BPF_PROG_TYPE_SOCKET_FILTER, 581 | insns, C.int(section.Size), 582 | (*C.char)(lp), C.int(version), 583 | (*C.char)(unsafe.Pointer(&b.log[0])), C.int(len(b.log))) 584 | if fd < 0 { 585 | panic(errors.New("Error while loading bpf prog")) 586 | } 587 | b.fd = int(fd) 588 | } 589 | } 590 | 591 | return nil 592 | } 593 | 594 | // Map returns the BPFMap for the given name. The name is the name used for 595 | // the map declaration with the MAP macro is the eBPF C file. 596 | func (b *BPFProg) Map(name string) *BPFMap { 597 | return b.maps[name] 598 | } 599 | 600 | // Maps returns a map of BPFMap indexed by their name 601 | func (b *BPFProg) Maps() map[string]*BPFMap { 602 | return b.maps 603 | } 604 | 605 | // Load loads the elf eBPF binary 606 | func (b *BPFProg) Load() error { 607 | if err := b.load(); err != nil { 608 | return err 609 | } 610 | return nil 611 | } 612 | 613 | // SetDefaultMaxEntries sets the default max_entries for all the maps 614 | // that will be loaded, if not defined the value 615 | func (b *BPFProg) SetDefaultMaxEntries(max int) { 616 | b.mapsDefaultMaxEntries = max 617 | } 618 | 619 | func (b *BPFProg) SetMaxEntries(table string, max int) { 620 | b.mapsMaxEntries[table] = max 621 | } 622 | 623 | // NewBPFProg returns a new BPFProg 624 | func NewBPFProg(r io.ReaderAt) (*BPFProg, error) { 625 | f, err := elf.NewFile(r) 626 | if err != nil { 627 | return nil, err 628 | } 629 | 630 | b := &BPFProg{ 631 | file: f, 632 | maps: make(map[string]*BPFMap), 633 | log: make([]byte, 65536), 634 | mapsMaxEntries: make(map[string]int), 635 | } 636 | 637 | return b, nil 638 | } 639 | --------------------------------------------------------------------------------