]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - samples/bpf/bpf_load.c
samples/bpf: make bpf_load.c code compatible with ELF maps section changes
[mirror_ubuntu-bionic-kernel.git] / samples / bpf / bpf_load.c
CommitLineData
249b812d
AS
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>
b896c4f9 11#include <stdlib.h>
249b812d
AS
12#include <linux/bpf.h>
13#include <linux/filter.h>
b896c4f9 14#include <linux/perf_event.h>
12d8bb64
MKL
15#include <linux/netlink.h>
16#include <linux/rtnetlink.h>
6387d011 17#include <linux/types.h>
12d8bb64
MKL
18#include <sys/types.h>
19#include <sys/socket.h>
b896c4f9
AS
20#include <sys/syscall.h>
21#include <sys/ioctl.h>
22#include <sys/mman.h>
23#include <poll.h>
5bacd780 24#include <ctype.h>
9fd63d05 25#include <assert.h>
249b812d 26#include "libbpf.h"
249b812d 27#include "bpf_load.h"
205c8ada 28#include "perf-sys.h"
249b812d 29
b896c4f9
AS
30#define DEBUGFS "/sys/kernel/debug/tracing/"
31
249b812d 32static char license[128];
b896c4f9 33static int kern_version;
249b812d 34static bool processed_sec[128];
d40fc181 35char bpf_log_buf[BPF_LOG_BUF_SIZE];
249b812d
AS
36int map_fd[MAX_MAPS];
37int prog_fd[MAX_PROGS];
b896c4f9 38int event_fd[MAX_PROGS];
249b812d 39int prog_cnt;
5bacd780
AS
40int prog_array_fd = -1;
41
156450d9
JDB
42/* Keeping relevant info on maps */
43struct bpf_map_data {
44 int fd;
45 char *name;
46 size_t elf_offset;
47 struct bpf_map_def def;
48};
49struct bpf_map_data map_data[MAX_MAPS];
50int map_data_count = 0;
51
5bacd780
AS
52static int populate_prog_array(const char *event, int prog_fd)
53{
54 int ind = atoi(event), err;
55
d40fc181 56 err = bpf_map_update_elem(prog_array_fd, &ind, &prog_fd, BPF_ANY);
5bacd780
AS
57 if (err < 0) {
58 printf("failed to store prog_fd in prog_array\n");
59 return -1;
60 }
61 return 0;
62}
249b812d
AS
63
64static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
65{
249b812d 66 bool is_socket = strncmp(event, "socket", 6) == 0;
b896c4f9
AS
67 bool is_kprobe = strncmp(event, "kprobe/", 7) == 0;
68 bool is_kretprobe = strncmp(event, "kretprobe/", 10) == 0;
c0766040 69 bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0;
86af8b41 70 bool is_xdp = strncmp(event, "xdp", 3) == 0;
1c47910e 71 bool is_perf_event = strncmp(event, "perf_event", 10) == 0;
4f2e7ae5
DA
72 bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0;
73 bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0;
43371c83 74 size_t insns_cnt = size / sizeof(struct bpf_insn);
b896c4f9
AS
75 enum bpf_prog_type prog_type;
76 char buf[256];
77 int fd, efd, err, id;
78 struct perf_event_attr attr = {};
79
80 attr.type = PERF_TYPE_TRACEPOINT;
81 attr.sample_type = PERF_SAMPLE_RAW;
82 attr.sample_period = 1;
83 attr.wakeup_events = 1;
84
85 if (is_socket) {
86 prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
87 } else if (is_kprobe || is_kretprobe) {
88 prog_type = BPF_PROG_TYPE_KPROBE;
c0766040
AS
89 } else if (is_tracepoint) {
90 prog_type = BPF_PROG_TYPE_TRACEPOINT;
86af8b41
BB
91 } else if (is_xdp) {
92 prog_type = BPF_PROG_TYPE_XDP;
1c47910e
AS
93 } else if (is_perf_event) {
94 prog_type = BPF_PROG_TYPE_PERF_EVENT;
4f2e7ae5
DA
95 } else if (is_cgroup_skb) {
96 prog_type = BPF_PROG_TYPE_CGROUP_SKB;
97 } else if (is_cgroup_sk) {
98 prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
b896c4f9
AS
99 } else {
100 printf("Unknown event '%s'\n", event);
249b812d 101 return -1;
b896c4f9
AS
102 }
103
43371c83 104 fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version,
d40fc181 105 bpf_log_buf, BPF_LOG_BUF_SIZE);
5bacd780 106 if (fd < 0) {
d40fc181 107 printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf);
5bacd780
AS
108 return -1;
109 }
110
111 prog_fd[prog_cnt++] = fd;
112
4f2e7ae5 113 if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
86af8b41
BB
114 return 0;
115
5bacd780
AS
116 if (is_socket) {
117 event += 6;
118 if (*event != '/')
119 return 0;
120 event++;
121 if (!isdigit(*event)) {
122 printf("invalid prog number\n");
123 return -1;
124 }
125 return populate_prog_array(event, fd);
126 }
127
b896c4f9
AS
128 if (is_kprobe || is_kretprobe) {
129 if (is_kprobe)
130 event += 7;
131 else
132 event += 10;
133
5bacd780
AS
134 if (*event == 0) {
135 printf("event name cannot be empty\n");
136 return -1;
137 }
138
139 if (isdigit(*event))
140 return populate_prog_array(event, fd);
141
b896c4f9
AS
142 snprintf(buf, sizeof(buf),
143 "echo '%c:%s %s' >> /sys/kernel/debug/tracing/kprobe_events",
144 is_kprobe ? 'p' : 'r', event, event);
145 err = system(buf);
146 if (err < 0) {
147 printf("failed to create kprobe '%s' error '%s'\n",
148 event, strerror(errno));
149 return -1;
150 }
249b812d 151
c0766040
AS
152 strcpy(buf, DEBUGFS);
153 strcat(buf, "events/kprobes/");
154 strcat(buf, event);
155 strcat(buf, "/id");
156 } else if (is_tracepoint) {
157 event += 11;
158
159 if (*event == 0) {
160 printf("event name cannot be empty\n");
161 return -1;
162 }
163 strcpy(buf, DEBUGFS);
164 strcat(buf, "events/");
165 strcat(buf, event);
166 strcat(buf, "/id");
167 }
b896c4f9
AS
168
169 efd = open(buf, O_RDONLY, 0);
170 if (efd < 0) {
171 printf("failed to open event %s\n", event);
172 return -1;
173 }
174
175 err = read(efd, buf, sizeof(buf));
176 if (err < 0 || err >= sizeof(buf)) {
177 printf("read from '%s' failed '%s'\n", event, strerror(errno));
178 return -1;
179 }
180
181 close(efd);
182
183 buf[err] = 0;
184 id = atoi(buf);
185 attr.config = id;
186
205c8ada 187 efd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0);
b896c4f9
AS
188 if (efd < 0) {
189 printf("event %d fd %d err %s\n", id, efd, strerror(errno));
190 return -1;
191 }
192 event_fd[prog_cnt - 1] = efd;
193 ioctl(efd, PERF_EVENT_IOC_ENABLE, 0);
194 ioctl(efd, PERF_EVENT_IOC_SET_BPF, fd);
195
249b812d
AS
196 return 0;
197}
198
156450d9
JDB
199static int load_maps(struct bpf_map_data *maps, int nr_maps,
200 fixup_map_cb fixup_map)
249b812d
AS
201{
202 int i;
156450d9 203
5010e948 204 for (i = 0; i < nr_maps; i++) {
9fd63d05 205 if (fixup_map)
156450d9 206 fixup_map(&maps[i].def, maps[i].name, i);
249b812d 207
156450d9
JDB
208 if (maps[i].def.type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
209 maps[i].def.type == BPF_MAP_TYPE_HASH_OF_MAPS) {
210 int inner_map_fd = map_fd[maps[i].def.inner_map_idx];
fb30d4b7 211
156450d9
JDB
212 map_fd[i] = bpf_create_map_in_map(maps[i].def.type,
213 maps[i].def.key_size,
214 inner_map_fd,
215 maps[i].def.max_entries,
216 maps[i].def.map_flags);
fb30d4b7 217 } else {
156450d9
JDB
218 map_fd[i] = bpf_create_map(maps[i].def.type,
219 maps[i].def.key_size,
220 maps[i].def.value_size,
221 maps[i].def.max_entries,
222 maps[i].def.map_flags);
fb30d4b7 223 }
618ec9a7
AS
224 if (map_fd[i] < 0) {
225 printf("failed to create a map: %d %s\n",
226 errno, strerror(errno));
249b812d 227 return 1;
618ec9a7 228 }
156450d9 229 maps[i].fd = map_fd[i];
5bacd780 230
156450d9 231 if (maps[i].def.type == BPF_MAP_TYPE_PROG_ARRAY)
5bacd780 232 prog_array_fd = map_fd[i];
249b812d
AS
233 }
234 return 0;
235}
236
237static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname,
238 GElf_Shdr *shdr, Elf_Data **data)
239{
240 Elf_Scn *scn;
241
242 scn = elf_getscn(elf, i);
243 if (!scn)
244 return 1;
245
246 if (gelf_getshdr(scn, shdr) != shdr)
247 return 2;
248
249 *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name);
250 if (!*shname || !shdr->sh_size)
251 return 3;
252
253 *data = elf_getdata(scn, 0);
254 if (!*data || elf_getdata(scn, *data) != NULL)
255 return 4;
256
257 return 0;
258}
259
260static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols,
156450d9
JDB
261 GElf_Shdr *shdr, struct bpf_insn *insn,
262 struct bpf_map_data *maps, int nr_maps)
249b812d
AS
263{
264 int i, nrels;
265
266 nrels = shdr->sh_size / shdr->sh_entsize;
267
268 for (i = 0; i < nrels; i++) {
269 GElf_Sym sym;
270 GElf_Rel rel;
271 unsigned int insn_idx;
156450d9
JDB
272 bool match = false;
273 int j, map_idx;
249b812d
AS
274
275 gelf_getrel(data, i, &rel);
276
277 insn_idx = rel.r_offset / sizeof(struct bpf_insn);
278
279 gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym);
280
281 if (insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
282 printf("invalid relo for insn[%d].code 0x%x\n",
283 insn_idx, insn[insn_idx].code);
284 return 1;
285 }
286 insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
156450d9
JDB
287
288 /* Match FD relocation against recorded map_data[] offset */
289 for (map_idx = 0; map_idx < nr_maps; map_idx++) {
290 if (maps[map_idx].elf_offset == sym.st_value) {
291 match = true;
292 break;
293 }
294 }
295 if (match) {
296 insn[insn_idx].imm = maps[map_idx].fd;
297 } else {
298 printf("invalid relo for insn[%d] no map_data match\n",
299 insn_idx);
300 return 1;
301 }
249b812d
AS
302 }
303
304 return 0;
305}
306
9fd63d05
MKL
307static int cmp_symbols(const void *l, const void *r)
308{
309 const GElf_Sym *lsym = (const GElf_Sym *)l;
310 const GElf_Sym *rsym = (const GElf_Sym *)r;
311
312 if (lsym->st_value < rsym->st_value)
313 return -1;
314 else if (lsym->st_value > rsym->st_value)
315 return 1;
316 else
317 return 0;
318}
319
156450d9
JDB
320static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
321 Elf *elf, Elf_Data *symbols, int strtabidx)
9fd63d05 322{
156450d9
JDB
323 int map_sz_elf, map_sz_copy;
324 bool validate_zero = false;
325 Elf_Data *data_maps;
326 int i, nr_maps;
327 GElf_Sym *sym;
328 Elf_Scn *scn;
329 int copy_sz;
330
331 if (maps_shndx < 0)
332 return -EINVAL;
333 if (!symbols)
334 return -EINVAL;
335
336 /* Get data for maps section via elf index */
337 scn = elf_getscn(elf, maps_shndx);
338 if (scn)
339 data_maps = elf_getdata(scn, NULL);
340 if (!scn || !data_maps) {
341 printf("Failed to get Elf_Data from maps section %d\n",
342 maps_shndx);
343 return -EINVAL;
344 }
9fd63d05 345
156450d9
JDB
346 /* For each map get corrosponding symbol table entry */
347 sym = calloc(MAX_MAPS+1, sizeof(GElf_Sym));
348 for (i = 0, nr_maps = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
349 assert(nr_maps < MAX_MAPS+1);
350 if (!gelf_getsym(symbols, i, &sym[nr_maps]))
9fd63d05 351 continue;
156450d9 352 if (sym[nr_maps].st_shndx != maps_shndx)
9fd63d05 353 continue;
156450d9 354 /* Only increment iif maps section */
9fd63d05
MKL
355 nr_maps++;
356 }
357
156450d9
JDB
358 /* Align to map_fd[] order, via sort on offset in sym.st_value */
359 qsort(sym, nr_maps, sizeof(GElf_Sym), cmp_symbols);
360
361 /* Keeping compatible with ELF maps section changes
362 * ------------------------------------------------
363 * The program size of struct bpf_map_def is known by loader
364 * code, but struct stored in ELF file can be different.
365 *
366 * Unfortunately sym[i].st_size is zero. To calculate the
367 * struct size stored in the ELF file, assume all struct have
368 * the same size, and simply divide with number of map
369 * symbols.
370 */
371 map_sz_elf = data_maps->d_size / nr_maps;
372 map_sz_copy = sizeof(struct bpf_map_def);
373 if (map_sz_elf < map_sz_copy) {
374 /*
375 * Backward compat, loading older ELF file with
376 * smaller struct, keeping remaining bytes zero.
377 */
378 map_sz_copy = map_sz_elf;
379 } else if (map_sz_elf > map_sz_copy) {
380 /*
381 * Forward compat, loading newer ELF file with larger
382 * struct with unknown features. Assume zero means
383 * feature not used. Thus, validate rest of struct
384 * data is zero.
385 */
386 validate_zero = true;
387 }
9fd63d05 388
156450d9 389 /* Memcpy relevant part of ELF maps data to loader maps */
9fd63d05 390 for (i = 0; i < nr_maps; i++) {
156450d9
JDB
391 unsigned char *addr, *end;
392 struct bpf_map_def *def;
393 const char *map_name;
394 size_t offset;
395
396 map_name = elf_strptr(elf, strtabidx, sym[i].st_name);
397 maps[i].name = strdup(map_name);
398 if (!maps[i].name) {
9fd63d05
MKL
399 printf("strdup(%s): %s(%d)\n", map_name,
400 strerror(errno), errno);
156450d9
JDB
401 free(sym);
402 return -errno;
403 }
404
405 /* Symbol value is offset into ELF maps section data area */
406 offset = sym[i].st_value;
407 def = (struct bpf_map_def *)(data_maps->d_buf + offset);
408 maps[i].elf_offset = offset;
409 memset(&maps[i].def, 0, sizeof(struct bpf_map_def));
410 memcpy(&maps[i].def, def, map_sz_copy);
411
412 /* Verify no newer features were requested */
413 if (validate_zero) {
414 addr = (unsigned char*) def + map_sz_copy;
415 end = (unsigned char*) def + map_sz_elf;
416 for (; addr < end; addr++) {
417 if (*addr != 0) {
418 free(sym);
419 return -EFBIG;
420 }
421 }
9fd63d05
MKL
422 }
423 }
424
156450d9 425 free(sym);
5010e948 426 return nr_maps;
9fd63d05
MKL
427}
428
429static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
249b812d 430{
9fd63d05 431 int fd, i, ret, maps_shndx = -1, strtabidx = -1;
249b812d
AS
432 Elf *elf;
433 GElf_Ehdr ehdr;
434 GElf_Shdr shdr, shdr_prog;
9fd63d05 435 Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL;
156450d9
JDB
436 char *shname, *shname_prog;
437 int nr_maps = 0;
249b812d 438
a734fb5d
MS
439 /* reset global variables */
440 kern_version = 0;
441 memset(license, 0, sizeof(license));
442 memset(processed_sec, 0, sizeof(processed_sec));
443
249b812d
AS
444 if (elf_version(EV_CURRENT) == EV_NONE)
445 return 1;
446
447 fd = open(path, O_RDONLY, 0);
448 if (fd < 0)
449 return 1;
450
451 elf = elf_begin(fd, ELF_C_READ, NULL);
452
453 if (!elf)
454 return 1;
455
456 if (gelf_getehdr(elf, &ehdr) != &ehdr)
457 return 1;
458
b896c4f9
AS
459 /* clear all kprobes */
460 i = system("echo \"\" > /sys/kernel/debug/tracing/kprobe_events");
461
249b812d
AS
462 /* scan over all elf sections to get license and map info */
463 for (i = 1; i < ehdr.e_shnum; i++) {
464
465 if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
466 continue;
467
468 if (0) /* helpful for llvm debugging */
469 printf("section %d:%s data %p size %zd link %d flags %d\n",
470 i, shname, data->d_buf, data->d_size,
471 shdr.sh_link, (int) shdr.sh_flags);
472
473 if (strcmp(shname, "license") == 0) {
474 processed_sec[i] = true;
475 memcpy(license, data->d_buf, data->d_size);
b896c4f9
AS
476 } else if (strcmp(shname, "version") == 0) {
477 processed_sec[i] = true;
478 if (data->d_size != sizeof(int)) {
479 printf("invalid size of version section %zd\n",
480 data->d_size);
481 return 1;
482 }
483 memcpy(&kern_version, data->d_buf, sizeof(int));
249b812d 484 } else if (strcmp(shname, "maps") == 0) {
156450d9
JDB
485 int j;
486
9fd63d05
MKL
487 maps_shndx = i;
488 data_maps = data;
156450d9
JDB
489 for (j = 0; j < MAX_MAPS; j++)
490 map_data[j].fd = -1;
249b812d 491 } else if (shdr.sh_type == SHT_SYMTAB) {
9fd63d05 492 strtabidx = shdr.sh_link;
249b812d
AS
493 symbols = data;
494 }
495 }
496
9fd63d05
MKL
497 ret = 1;
498
499 if (!symbols) {
500 printf("missing SHT_SYMTAB section\n");
501 goto done;
502 }
503
504 if (data_maps) {
156450d9
JDB
505 nr_maps = load_elf_maps_section(map_data, maps_shndx,
506 elf, symbols, strtabidx);
507 if (nr_maps < 0) {
508 printf("Error: Failed loading ELF maps (errno:%d):%s\n",
509 nr_maps, strerror(-nr_maps));
5010e948
JDB
510 ret = 1;
511 goto done;
512 }
156450d9 513 if (load_maps(map_data, nr_maps, fixup_map))
9fd63d05 514 goto done;
156450d9 515 map_data_count = nr_maps;
9fd63d05
MKL
516
517 processed_sec[maps_shndx] = true;
518 }
519
249b812d
AS
520 /* load programs that need map fixup (relocations) */
521 for (i = 1; i < ehdr.e_shnum; i++) {
16ad1329
MS
522 if (processed_sec[i])
523 continue;
249b812d
AS
524
525 if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
526 continue;
527 if (shdr.sh_type == SHT_REL) {
528 struct bpf_insn *insns;
529
530 if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog,
531 &shdr_prog, &data_prog))
532 continue;
533
db6a71dd
AS
534 if (shdr_prog.sh_type != SHT_PROGBITS ||
535 !(shdr_prog.sh_flags & SHF_EXECINSTR))
536 continue;
537
249b812d
AS
538 insns = (struct bpf_insn *) data_prog->d_buf;
539
540 processed_sec[shdr.sh_info] = true;
541 processed_sec[i] = true;
542
156450d9
JDB
543 if (parse_relo_and_apply(data, symbols, &shdr, insns,
544 map_data, nr_maps))
249b812d
AS
545 continue;
546
b896c4f9
AS
547 if (memcmp(shname_prog, "kprobe/", 7) == 0 ||
548 memcmp(shname_prog, "kretprobe/", 10) == 0 ||
c0766040 549 memcmp(shname_prog, "tracepoint/", 11) == 0 ||
86af8b41 550 memcmp(shname_prog, "xdp", 3) == 0 ||
1c47910e 551 memcmp(shname_prog, "perf_event", 10) == 0 ||
4f2e7ae5
DA
552 memcmp(shname_prog, "socket", 6) == 0 ||
553 memcmp(shname_prog, "cgroup/", 7) == 0)
249b812d
AS
554 load_and_attach(shname_prog, insns, data_prog->d_size);
555 }
556 }
557
558 /* load programs that don't use maps */
559 for (i = 1; i < ehdr.e_shnum; i++) {
560
561 if (processed_sec[i])
562 continue;
563
564 if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
565 continue;
566
b896c4f9
AS
567 if (memcmp(shname, "kprobe/", 7) == 0 ||
568 memcmp(shname, "kretprobe/", 10) == 0 ||
c0766040 569 memcmp(shname, "tracepoint/", 11) == 0 ||
86af8b41 570 memcmp(shname, "xdp", 3) == 0 ||
1c47910e 571 memcmp(shname, "perf_event", 10) == 0 ||
4f2e7ae5
DA
572 memcmp(shname, "socket", 6) == 0 ||
573 memcmp(shname, "cgroup/", 7) == 0)
249b812d
AS
574 load_and_attach(shname, data->d_buf, data->d_size);
575 }
576
9fd63d05
MKL
577 ret = 0;
578done:
249b812d 579 close(fd);
9fd63d05
MKL
580 return ret;
581}
582
583int load_bpf_file(char *path)
584{
585 return do_load_bpf_file(path, NULL);
586}
587
588int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map)
589{
590 return do_load_bpf_file(path, fixup_map);
249b812d 591}
b896c4f9
AS
592
593void read_trace_pipe(void)
594{
595 int trace_fd;
596
597 trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
598 if (trace_fd < 0)
599 return;
600
601 while (1) {
602 static char buf[4096];
603 ssize_t sz;
604
605 sz = read(trace_fd, buf, sizeof(buf));
606 if (sz > 0) {
607 buf[sz] = 0;
608 puts(buf);
609 }
610 }
611}
3622e7e4
AS
612
613#define MAX_SYMS 300000
614static struct ksym syms[MAX_SYMS];
615static int sym_cnt;
616
617static int ksym_cmp(const void *p1, const void *p2)
618{
619 return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
620}
621
622int load_kallsyms(void)
623{
624 FILE *f = fopen("/proc/kallsyms", "r");
625 char func[256], buf[256];
626 char symbol;
627 void *addr;
628 int i = 0;
629
630 if (!f)
631 return -ENOENT;
632
633 while (!feof(f)) {
634 if (!fgets(buf, sizeof(buf), f))
635 break;
636 if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
637 break;
638 if (!addr)
639 continue;
640 syms[i].addr = (long) addr;
641 syms[i].name = strdup(func);
642 i++;
643 }
644 sym_cnt = i;
645 qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
646 return 0;
647}
648
649struct ksym *ksym_search(long key)
650{
651 int start = 0, end = sym_cnt;
652 int result;
653
654 while (start < end) {
655 size_t mid = start + (end - start) / 2;
656
657 result = key - syms[mid].addr;
658 if (result < 0)
659 end = mid;
660 else if (result > 0)
661 start = mid + 1;
662 else
663 return &syms[mid];
664 }
665
666 if (start >= 1 && syms[start - 1].addr < key &&
667 key < syms[start].addr)
668 /* valid ksym */
669 return &syms[start - 1];
670
671 /* out of range. return _stext */
672 return &syms[0];
673}
12d8bb64 674
6387d011 675int set_link_xdp_fd(int ifindex, int fd, __u32 flags)
12d8bb64
MKL
676{
677 struct sockaddr_nl sa;
678 int sock, seq = 0, len, ret = -1;
679 char buf[4096];
680 struct nlattr *nla, *nla_xdp;
681 struct {
682 struct nlmsghdr nh;
683 struct ifinfomsg ifinfo;
684 char attrbuf[64];
685 } req;
686 struct nlmsghdr *nh;
687 struct nlmsgerr *err;
688
689 memset(&sa, 0, sizeof(sa));
690 sa.nl_family = AF_NETLINK;
691
692 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
693 if (sock < 0) {
694 printf("open netlink socket: %s\n", strerror(errno));
695 return -1;
696 }
697
698 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
699 printf("bind to netlink: %s\n", strerror(errno));
700 goto cleanup;
701 }
702
703 memset(&req, 0, sizeof(req));
704 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
705 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
706 req.nh.nlmsg_type = RTM_SETLINK;
707 req.nh.nlmsg_pid = 0;
708 req.nh.nlmsg_seq = ++seq;
709 req.ifinfo.ifi_family = AF_UNSPEC;
710 req.ifinfo.ifi_index = ifindex;
3993f2cb
DA
711
712 /* started nested attribute for XDP */
12d8bb64
MKL
713 nla = (struct nlattr *)(((char *)&req)
714 + NLMSG_ALIGN(req.nh.nlmsg_len));
715 nla->nla_type = NLA_F_NESTED | 43/*IFLA_XDP*/;
3993f2cb 716 nla->nla_len = NLA_HDRLEN;
12d8bb64 717
3993f2cb
DA
718 /* add XDP fd */
719 nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
12d8bb64
MKL
720 nla_xdp->nla_type = 1/*IFLA_XDP_FD*/;
721 nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
722 memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
3993f2cb
DA
723 nla->nla_len += nla_xdp->nla_len;
724
725 /* if user passed in any flags, add those too */
726 if (flags) {
727 nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
728 nla_xdp->nla_type = 3/*IFLA_XDP_FLAGS*/;
729 nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
730 memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
731 nla->nla_len += nla_xdp->nla_len;
732 }
12d8bb64
MKL
733
734 req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
735
736 if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
737 printf("send to netlink: %s\n", strerror(errno));
738 goto cleanup;
739 }
740
741 len = recv(sock, buf, sizeof(buf), 0);
742 if (len < 0) {
743 printf("recv from netlink: %s\n", strerror(errno));
744 goto cleanup;
745 }
746
747 for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
748 nh = NLMSG_NEXT(nh, len)) {
749 if (nh->nlmsg_pid != getpid()) {
750 printf("Wrong pid %d, expected %d\n",
751 nh->nlmsg_pid, getpid());
752 goto cleanup;
753 }
754 if (nh->nlmsg_seq != seq) {
755 printf("Wrong seq %d, expected %d\n",
756 nh->nlmsg_seq, seq);
757 goto cleanup;
758 }
759 switch (nh->nlmsg_type) {
760 case NLMSG_ERROR:
761 err = (struct nlmsgerr *)NLMSG_DATA(nh);
762 if (!err->error)
763 continue;
764 printf("nlmsg error %s\n", strerror(-err->error));
765 goto cleanup;
766 case NLMSG_DONE:
767 break;
768 }
769 }
770
771 ret = 0;
772
773cleanup:
774 close(sock);
775 return ret;
776}