diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2014-12-01 18:06:37 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-12-06 00:47:33 -0500 |
commit | 249b812d8005ec38e351ee763ceb85d56b155064 (patch) | |
tree | b81980d495f62e9a167ab75e132da0d387713179 /samples | |
parent | 03f4723ed7a52bd31da26eefe2cdde563ea0f468 (diff) |
samples: bpf: elf_bpf file loader
simple .o parser and loader using BPF syscall.
.o is a standard ELF generated by LLVM backend
It parses elf file compiled by llvm .c->.o
- parses 'maps' section and creates maps via BPF syscall
- parses 'license' section and passes it to syscall
- parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns
by storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD
- loads eBPF programs via BPF syscall
One ELF file can contain multiple BPF programs.
int load_bpf_file(char *path);
populates prog_fd[] and map_fd[] with FDs received from bpf syscall
bpf_helpers.h - helper functions available to eBPF programs written in C
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
-rw-r--r-- | samples/bpf/bpf_helpers.h | 40 | ||||
-rw-r--r-- | samples/bpf/bpf_load.c | 203 | ||||
-rw-r--r-- | samples/bpf/bpf_load.h | 24 |
3 files changed, 267 insertions, 0 deletions
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h new file mode 100644 index 000000000000..ca0333146006 --- /dev/null +++ b/samples/bpf/bpf_helpers.h | |||
@@ -0,0 +1,40 @@ | |||
1 | #ifndef __BPF_HELPERS_H | ||
2 | #define __BPF_HELPERS_H | ||
3 | |||
4 | /* helper macro to place programs, maps, license in | ||
5 | * different sections in elf_bpf file. Section names | ||
6 | * are interpreted by elf_bpf loader | ||
7 | */ | ||
8 | #define SEC(NAME) __attribute__((section(NAME), used)) | ||
9 | |||
10 | /* helper functions called from eBPF programs written in C */ | ||
11 | static void *(*bpf_map_lookup_elem)(void *map, void *key) = | ||
12 | (void *) BPF_FUNC_map_lookup_elem; | ||
13 | static int (*bpf_map_update_elem)(void *map, void *key, void *value, | ||
14 | unsigned long long flags) = | ||
15 | (void *) BPF_FUNC_map_update_elem; | ||
16 | static int (*bpf_map_delete_elem)(void *map, void *key) = | ||
17 | (void *) BPF_FUNC_map_delete_elem; | ||
18 | |||
19 | /* llvm builtin functions that eBPF C program may use to | ||
20 | * emit BPF_LD_ABS and BPF_LD_IND instructions | ||
21 | */ | ||
22 | struct sk_buff; | ||
23 | unsigned long long load_byte(void *skb, | ||
24 | unsigned long long off) asm("llvm.bpf.load.byte"); | ||
25 | unsigned long long load_half(void *skb, | ||
26 | unsigned long long off) asm("llvm.bpf.load.half"); | ||
27 | unsigned long long load_word(void *skb, | ||
28 | unsigned long long off) asm("llvm.bpf.load.word"); | ||
29 | |||
30 | /* a helper structure used by eBPF C program | ||
31 | * to describe map attributes to elf_bpf loader | ||
32 | */ | ||
33 | struct bpf_map_def { | ||
34 | unsigned int type; | ||
35 | unsigned int key_size; | ||
36 | unsigned int value_size; | ||
37 | unsigned int max_entries; | ||
38 | }; | ||
39 | |||
40 | #endif | ||
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c new file mode 100644 index 000000000000..1831d236382b --- /dev/null +++ b/samples/bpf/bpf_load.c | |||
@@ -0,0 +1,203 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <libelf.h> | ||
6 | #include <gelf.h> | ||
7 | #include <errno.h> | ||
8 | #include <unistd.h> | ||
9 | #include <string.h> | ||
10 | #include <stdbool.h> | ||
11 | #include <linux/bpf.h> | ||
12 | #include <linux/filter.h> | ||
13 | #include "libbpf.h" | ||
14 | #include "bpf_helpers.h" | ||
15 | #include "bpf_load.h" | ||
16 | |||
17 | static char license[128]; | ||
18 | static bool processed_sec[128]; | ||
19 | int map_fd[MAX_MAPS]; | ||
20 | int prog_fd[MAX_PROGS]; | ||
21 | int prog_cnt; | ||
22 | |||
23 | static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||
24 | { | ||
25 | int fd; | ||
26 | bool is_socket = strncmp(event, "socket", 6) == 0; | ||
27 | |||
28 | if (!is_socket) | ||
29 | /* tracing events tbd */ | ||
30 | return -1; | ||
31 | |||
32 | fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, | ||
33 | prog, size, license); | ||
34 | |||
35 | if (fd < 0) { | ||
36 | printf("bpf_prog_load() err=%d\n%s", errno, bpf_log_buf); | ||
37 | return -1; | ||
38 | } | ||
39 | |||
40 | prog_fd[prog_cnt++] = fd; | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static int load_maps(struct bpf_map_def *maps, int len) | ||
46 | { | ||
47 | int i; | ||
48 | |||
49 | for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { | ||
50 | |||
51 | map_fd[i] = bpf_create_map(maps[i].type, | ||
52 | maps[i].key_size, | ||
53 | maps[i].value_size, | ||
54 | maps[i].max_entries); | ||
55 | if (map_fd[i] < 0) | ||
56 | return 1; | ||
57 | } | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname, | ||
62 | GElf_Shdr *shdr, Elf_Data **data) | ||
63 | { | ||
64 | Elf_Scn *scn; | ||
65 | |||
66 | scn = elf_getscn(elf, i); | ||
67 | if (!scn) | ||
68 | return 1; | ||
69 | |||
70 | if (gelf_getshdr(scn, shdr) != shdr) | ||
71 | return 2; | ||
72 | |||
73 | *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name); | ||
74 | if (!*shname || !shdr->sh_size) | ||
75 | return 3; | ||
76 | |||
77 | *data = elf_getdata(scn, 0); | ||
78 | if (!*data || elf_getdata(scn, *data) != NULL) | ||
79 | return 4; | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, | ||
85 | GElf_Shdr *shdr, struct bpf_insn *insn) | ||
86 | { | ||
87 | int i, nrels; | ||
88 | |||
89 | nrels = shdr->sh_size / shdr->sh_entsize; | ||
90 | |||
91 | for (i = 0; i < nrels; i++) { | ||
92 | GElf_Sym sym; | ||
93 | GElf_Rel rel; | ||
94 | unsigned int insn_idx; | ||
95 | |||
96 | gelf_getrel(data, i, &rel); | ||
97 | |||
98 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); | ||
99 | |||
100 | gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym); | ||
101 | |||
102 | if (insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { | ||
103 | printf("invalid relo for insn[%d].code 0x%x\n", | ||
104 | insn_idx, insn[insn_idx].code); | ||
105 | return 1; | ||
106 | } | ||
107 | insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | ||
108 | insn[insn_idx].imm = map_fd[sym.st_value / sizeof(struct bpf_map_def)]; | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | int load_bpf_file(char *path) | ||
115 | { | ||
116 | int fd, i; | ||
117 | Elf *elf; | ||
118 | GElf_Ehdr ehdr; | ||
119 | GElf_Shdr shdr, shdr_prog; | ||
120 | Elf_Data *data, *data_prog, *symbols = NULL; | ||
121 | char *shname, *shname_prog; | ||
122 | |||
123 | if (elf_version(EV_CURRENT) == EV_NONE) | ||
124 | return 1; | ||
125 | |||
126 | fd = open(path, O_RDONLY, 0); | ||
127 | if (fd < 0) | ||
128 | return 1; | ||
129 | |||
130 | elf = elf_begin(fd, ELF_C_READ, NULL); | ||
131 | |||
132 | if (!elf) | ||
133 | return 1; | ||
134 | |||
135 | if (gelf_getehdr(elf, &ehdr) != &ehdr) | ||
136 | return 1; | ||
137 | |||
138 | /* scan over all elf sections to get license and map info */ | ||
139 | for (i = 1; i < ehdr.e_shnum; i++) { | ||
140 | |||
141 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) | ||
142 | continue; | ||
143 | |||
144 | if (0) /* helpful for llvm debugging */ | ||
145 | printf("section %d:%s data %p size %zd link %d flags %d\n", | ||
146 | i, shname, data->d_buf, data->d_size, | ||
147 | shdr.sh_link, (int) shdr.sh_flags); | ||
148 | |||
149 | if (strcmp(shname, "license") == 0) { | ||
150 | processed_sec[i] = true; | ||
151 | memcpy(license, data->d_buf, data->d_size); | ||
152 | } else if (strcmp(shname, "maps") == 0) { | ||
153 | processed_sec[i] = true; | ||
154 | if (load_maps(data->d_buf, data->d_size)) | ||
155 | return 1; | ||
156 | } else if (shdr.sh_type == SHT_SYMTAB) { | ||
157 | symbols = data; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | /* load programs that need map fixup (relocations) */ | ||
162 | for (i = 1; i < ehdr.e_shnum; i++) { | ||
163 | |||
164 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) | ||
165 | continue; | ||
166 | if (shdr.sh_type == SHT_REL) { | ||
167 | struct bpf_insn *insns; | ||
168 | |||
169 | if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog, | ||
170 | &shdr_prog, &data_prog)) | ||
171 | continue; | ||
172 | |||
173 | insns = (struct bpf_insn *) data_prog->d_buf; | ||
174 | |||
175 | processed_sec[shdr.sh_info] = true; | ||
176 | processed_sec[i] = true; | ||
177 | |||
178 | if (parse_relo_and_apply(data, symbols, &shdr, insns)) | ||
179 | continue; | ||
180 | |||
181 | if (memcmp(shname_prog, "events/", 7) == 0 || | ||
182 | memcmp(shname_prog, "socket", 6) == 0) | ||
183 | load_and_attach(shname_prog, insns, data_prog->d_size); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* load programs that don't use maps */ | ||
188 | for (i = 1; i < ehdr.e_shnum; i++) { | ||
189 | |||
190 | if (processed_sec[i]) | ||
191 | continue; | ||
192 | |||
193 | if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) | ||
194 | continue; | ||
195 | |||
196 | if (memcmp(shname, "events/", 7) == 0 || | ||
197 | memcmp(shname, "socket", 6) == 0) | ||
198 | load_and_attach(shname, data->d_buf, data->d_size); | ||
199 | } | ||
200 | |||
201 | close(fd); | ||
202 | return 0; | ||
203 | } | ||
diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h new file mode 100644 index 000000000000..27789a34f5e6 --- /dev/null +++ b/samples/bpf/bpf_load.h | |||
@@ -0,0 +1,24 @@ | |||
1 | #ifndef __BPF_LOAD_H | ||
2 | #define __BPF_LOAD_H | ||
3 | |||
4 | #define MAX_MAPS 32 | ||
5 | #define MAX_PROGS 32 | ||
6 | |||
7 | extern int map_fd[MAX_MAPS]; | ||
8 | extern int prog_fd[MAX_PROGS]; | ||
9 | |||
10 | /* parses elf file compiled by llvm .c->.o | ||
11 | * . parses 'maps' section and creates maps via BPF syscall | ||
12 | * . parses 'license' section and passes it to syscall | ||
13 | * . parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by | ||
14 | * storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD | ||
15 | * . loads eBPF programs via BPF syscall | ||
16 | * | ||
17 | * One ELF file can contain multiple BPF programs which will be loaded | ||
18 | * and their FDs stored stored in prog_fd array | ||
19 | * | ||
20 | * returns zero on success | ||
21 | */ | ||
22 | int load_bpf_file(char *path); | ||
23 | |||
24 | #endif | ||