diff options
Diffstat (limited to 'tools/lib/bpf/bpf.c')
-rw-r--r-- | tools/lib/bpf/bpf.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 5128677e4117..592a58a2b681 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c | |||
@@ -1,3 +1,5 @@ | |||
1 | // SPDX-License-Identifier: LGPL-2.1 | ||
2 | |||
1 | /* | 3 | /* |
2 | * common eBPF ELF operations. | 4 | * common eBPF ELF operations. |
3 | * | 5 | * |
@@ -25,6 +27,16 @@ | |||
25 | #include <asm/unistd.h> | 27 | #include <asm/unistd.h> |
26 | #include <linux/bpf.h> | 28 | #include <linux/bpf.h> |
27 | #include "bpf.h" | 29 | #include "bpf.h" |
30 | #include "libbpf.h" | ||
31 | #include "nlattr.h" | ||
32 | #include <linux/rtnetlink.h> | ||
33 | #include <linux/if_link.h> | ||
34 | #include <sys/socket.h> | ||
35 | #include <errno.h> | ||
36 | |||
37 | #ifndef SOL_NETLINK | ||
38 | #define SOL_NETLINK 270 | ||
39 | #endif | ||
28 | 40 | ||
29 | /* | 41 | /* |
30 | * When building perf, unistd.h is overridden. __NR_bpf is | 42 | * When building perf, unistd.h is overridden. __NR_bpf is |
@@ -46,7 +58,9 @@ | |||
46 | # endif | 58 | # endif |
47 | #endif | 59 | #endif |
48 | 60 | ||
61 | #ifndef min | ||
49 | #define min(x, y) ((x) < (y) ? (x) : (y)) | 62 | #define min(x, y) ((x) < (y) ? (x) : (y)) |
63 | #endif | ||
50 | 64 | ||
51 | static inline __u64 ptr_to_u64(const void *ptr) | 65 | static inline __u64 ptr_to_u64(const void *ptr) |
52 | { | 66 | { |
@@ -413,3 +427,124 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len) | |||
413 | 427 | ||
414 | return err; | 428 | return err; |
415 | } | 429 | } |
430 | |||
431 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) | ||
432 | { | ||
433 | struct sockaddr_nl sa; | ||
434 | int sock, seq = 0, len, ret = -1; | ||
435 | char buf[4096]; | ||
436 | struct nlattr *nla, *nla_xdp; | ||
437 | struct { | ||
438 | struct nlmsghdr nh; | ||
439 | struct ifinfomsg ifinfo; | ||
440 | char attrbuf[64]; | ||
441 | } req; | ||
442 | struct nlmsghdr *nh; | ||
443 | struct nlmsgerr *err; | ||
444 | socklen_t addrlen; | ||
445 | int one = 1; | ||
446 | |||
447 | memset(&sa, 0, sizeof(sa)); | ||
448 | sa.nl_family = AF_NETLINK; | ||
449 | |||
450 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||
451 | if (sock < 0) { | ||
452 | return -errno; | ||
453 | } | ||
454 | |||
455 | if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, | ||
456 | &one, sizeof(one)) < 0) { | ||
457 | fprintf(stderr, "Netlink error reporting not supported\n"); | ||
458 | } | ||
459 | |||
460 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { | ||
461 | ret = -errno; | ||
462 | goto cleanup; | ||
463 | } | ||
464 | |||
465 | addrlen = sizeof(sa); | ||
466 | if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { | ||
467 | ret = -errno; | ||
468 | goto cleanup; | ||
469 | } | ||
470 | |||
471 | if (addrlen != sizeof(sa)) { | ||
472 | ret = -LIBBPF_ERRNO__INTERNAL; | ||
473 | goto cleanup; | ||
474 | } | ||
475 | |||
476 | memset(&req, 0, sizeof(req)); | ||
477 | req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | ||
478 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | ||
479 | req.nh.nlmsg_type = RTM_SETLINK; | ||
480 | req.nh.nlmsg_pid = 0; | ||
481 | req.nh.nlmsg_seq = ++seq; | ||
482 | req.ifinfo.ifi_family = AF_UNSPEC; | ||
483 | req.ifinfo.ifi_index = ifindex; | ||
484 | |||
485 | /* started nested attribute for XDP */ | ||
486 | nla = (struct nlattr *)(((char *)&req) | ||
487 | + NLMSG_ALIGN(req.nh.nlmsg_len)); | ||
488 | nla->nla_type = NLA_F_NESTED | IFLA_XDP; | ||
489 | nla->nla_len = NLA_HDRLEN; | ||
490 | |||
491 | /* add XDP fd */ | ||
492 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
493 | nla_xdp->nla_type = IFLA_XDP_FD; | ||
494 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); | ||
495 | memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); | ||
496 | nla->nla_len += nla_xdp->nla_len; | ||
497 | |||
498 | /* if user passed in any flags, add those too */ | ||
499 | if (flags) { | ||
500 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
501 | nla_xdp->nla_type = IFLA_XDP_FLAGS; | ||
502 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); | ||
503 | memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); | ||
504 | nla->nla_len += nla_xdp->nla_len; | ||
505 | } | ||
506 | |||
507 | req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); | ||
508 | |||
509 | if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { | ||
510 | ret = -errno; | ||
511 | goto cleanup; | ||
512 | } | ||
513 | |||
514 | len = recv(sock, buf, sizeof(buf), 0); | ||
515 | if (len < 0) { | ||
516 | ret = -errno; | ||
517 | goto cleanup; | ||
518 | } | ||
519 | |||
520 | for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); | ||
521 | nh = NLMSG_NEXT(nh, len)) { | ||
522 | if (nh->nlmsg_pid != sa.nl_pid) { | ||
523 | ret = -LIBBPF_ERRNO__WRNGPID; | ||
524 | goto cleanup; | ||
525 | } | ||
526 | if (nh->nlmsg_seq != seq) { | ||
527 | ret = -LIBBPF_ERRNO__INVSEQ; | ||
528 | goto cleanup; | ||
529 | } | ||
530 | switch (nh->nlmsg_type) { | ||
531 | case NLMSG_ERROR: | ||
532 | err = (struct nlmsgerr *)NLMSG_DATA(nh); | ||
533 | if (!err->error) | ||
534 | continue; | ||
535 | ret = err->error; | ||
536 | nla_dump_errormsg(nh); | ||
537 | goto cleanup; | ||
538 | case NLMSG_DONE: | ||
539 | break; | ||
540 | default: | ||
541 | break; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | ret = 0; | ||
546 | |||
547 | cleanup: | ||
548 | close(sock); | ||
549 | return ret; | ||
550 | } | ||