diff options
author | Eric Leblond <eric@regit.org> | 2018-01-30 15:55:01 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-02-02 20:53:48 -0500 |
commit | 949abbe88436c000cc63fce2bdfeb48b7d06a7df (patch) | |
tree | 0c92cc22fd100260e510f79695e64dbb554eeb8d /tools/lib/bpf/bpf.c | |
parent | dc2b9f19e3bdaa87a7c3d123b8bba8a42d96d942 (diff) |
libbpf: add function to setup XDP
Most of the code is taken from set_link_xdp_fd() in bpf_load.c and
slightly modified to be library compliant.
Signed-off-by: Eric Leblond <eric@regit.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools/lib/bpf/bpf.c')
-rw-r--r-- | tools/lib/bpf/bpf.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 5128677e4117..bf2772566240 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c | |||
@@ -25,6 +25,12 @@ | |||
25 | #include <asm/unistd.h> | 25 | #include <asm/unistd.h> |
26 | #include <linux/bpf.h> | 26 | #include <linux/bpf.h> |
27 | #include "bpf.h" | 27 | #include "bpf.h" |
28 | #include "libbpf.h" | ||
29 | #include "nlattr.h" | ||
30 | #include <linux/rtnetlink.h> | ||
31 | #include <linux/if_link.h> | ||
32 | #include <sys/socket.h> | ||
33 | #include <errno.h> | ||
28 | 34 | ||
29 | /* | 35 | /* |
30 | * When building perf, unistd.h is overridden. __NR_bpf is | 36 | * When building perf, unistd.h is overridden. __NR_bpf is |
@@ -46,7 +52,9 @@ | |||
46 | # endif | 52 | # endif |
47 | #endif | 53 | #endif |
48 | 54 | ||
55 | #ifndef min | ||
49 | #define min(x, y) ((x) < (y) ? (x) : (y)) | 56 | #define min(x, y) ((x) < (y) ? (x) : (y)) |
57 | #endif | ||
50 | 58 | ||
51 | static inline __u64 ptr_to_u64(const void *ptr) | 59 | static inline __u64 ptr_to_u64(const void *ptr) |
52 | { | 60 | { |
@@ -413,3 +421,117 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len) | |||
413 | 421 | ||
414 | return err; | 422 | return err; |
415 | } | 423 | } |
424 | |||
425 | int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) | ||
426 | { | ||
427 | struct sockaddr_nl sa; | ||
428 | int sock, seq = 0, len, ret = -1; | ||
429 | char buf[4096]; | ||
430 | struct nlattr *nla, *nla_xdp; | ||
431 | struct { | ||
432 | struct nlmsghdr nh; | ||
433 | struct ifinfomsg ifinfo; | ||
434 | char attrbuf[64]; | ||
435 | } req; | ||
436 | struct nlmsghdr *nh; | ||
437 | struct nlmsgerr *err; | ||
438 | socklen_t addrlen; | ||
439 | |||
440 | memset(&sa, 0, sizeof(sa)); | ||
441 | sa.nl_family = AF_NETLINK; | ||
442 | |||
443 | sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | ||
444 | if (sock < 0) { | ||
445 | return -errno; | ||
446 | } | ||
447 | |||
448 | if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { | ||
449 | ret = -errno; | ||
450 | goto cleanup; | ||
451 | } | ||
452 | |||
453 | addrlen = sizeof(sa); | ||
454 | if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { | ||
455 | ret = -errno; | ||
456 | goto cleanup; | ||
457 | } | ||
458 | |||
459 | if (addrlen != sizeof(sa)) { | ||
460 | ret = -LIBBPF_ERRNO__INTERNAL; | ||
461 | goto cleanup; | ||
462 | } | ||
463 | |||
464 | memset(&req, 0, sizeof(req)); | ||
465 | req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | ||
466 | req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | ||
467 | req.nh.nlmsg_type = RTM_SETLINK; | ||
468 | req.nh.nlmsg_pid = 0; | ||
469 | req.nh.nlmsg_seq = ++seq; | ||
470 | req.ifinfo.ifi_family = AF_UNSPEC; | ||
471 | req.ifinfo.ifi_index = ifindex; | ||
472 | |||
473 | /* started nested attribute for XDP */ | ||
474 | nla = (struct nlattr *)(((char *)&req) | ||
475 | + NLMSG_ALIGN(req.nh.nlmsg_len)); | ||
476 | nla->nla_type = NLA_F_NESTED | IFLA_XDP; | ||
477 | nla->nla_len = NLA_HDRLEN; | ||
478 | |||
479 | /* add XDP fd */ | ||
480 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
481 | nla_xdp->nla_type = IFLA_XDP_FD; | ||
482 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); | ||
483 | memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); | ||
484 | nla->nla_len += nla_xdp->nla_len; | ||
485 | |||
486 | /* if user passed in any flags, add those too */ | ||
487 | if (flags) { | ||
488 | nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); | ||
489 | nla_xdp->nla_type = IFLA_XDP_FLAGS; | ||
490 | nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); | ||
491 | memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); | ||
492 | nla->nla_len += nla_xdp->nla_len; | ||
493 | } | ||
494 | |||
495 | req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); | ||
496 | |||
497 | if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { | ||
498 | ret = -errno; | ||
499 | goto cleanup; | ||
500 | } | ||
501 | |||
502 | len = recv(sock, buf, sizeof(buf), 0); | ||
503 | if (len < 0) { | ||
504 | ret = -errno; | ||
505 | goto cleanup; | ||
506 | } | ||
507 | |||
508 | for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); | ||
509 | nh = NLMSG_NEXT(nh, len)) { | ||
510 | if (nh->nlmsg_pid != sa.nl_pid) { | ||
511 | ret = -LIBBPF_ERRNO__WRNGPID; | ||
512 | goto cleanup; | ||
513 | } | ||
514 | if (nh->nlmsg_seq != seq) { | ||
515 | ret = -LIBBPF_ERRNO__INVSEQ; | ||
516 | goto cleanup; | ||
517 | } | ||
518 | switch (nh->nlmsg_type) { | ||
519 | case NLMSG_ERROR: | ||
520 | err = (struct nlmsgerr *)NLMSG_DATA(nh); | ||
521 | if (!err->error) | ||
522 | continue; | ||
523 | ret = err->error; | ||
524 | goto cleanup; | ||
525 | case NLMSG_DONE: | ||
526 | break; | ||
527 | default: | ||
528 | break; | ||
529 | } | ||
530 | } | ||
531 | |||
532 | ret = 0; | ||
533 | |||
534 | cleanup: | ||
535 | close(sock); | ||
536 | return ret; | ||
537 | } | ||