Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Eric Leblond and committed by
Alexei Starovoitov
949abbe8 dc2b9f19

+128
+122
tools/lib/bpf/bpf.c
··· 25 25 #include <asm/unistd.h> 26 26 #include <linux/bpf.h> 27 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 36 * When building perf, unistd.h is overridden. __NR_bpf is ··· 52 46 # endif 53 47 #endif 54 48 49 + #ifndef min 55 50 #define min(x, y) ((x) < (y) ? (x) : (y)) 51 + #endif 56 52 57 53 static inline __u64 ptr_to_u64(const void *ptr) 58 54 { ··· 420 412 *info_len = attr.info.info_len; 421 413 422 414 return err; 415 + } 416 + 417 + int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 418 + { 419 + struct sockaddr_nl sa; 420 + int sock, seq = 0, len, ret = -1; 421 + char buf[4096]; 422 + struct nlattr *nla, *nla_xdp; 423 + struct { 424 + struct nlmsghdr nh; 425 + struct ifinfomsg ifinfo; 426 + char attrbuf[64]; 427 + } req; 428 + struct nlmsghdr *nh; 429 + struct nlmsgerr *err; 430 + socklen_t addrlen; 431 + 432 + memset(&sa, 0, sizeof(sa)); 433 + sa.nl_family = AF_NETLINK; 434 + 435 + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 436 + if (sock < 0) { 437 + return -errno; 438 + } 439 + 440 + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 441 + ret = -errno; 442 + goto cleanup; 443 + } 444 + 445 + addrlen = sizeof(sa); 446 + if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 447 + ret = -errno; 448 + goto cleanup; 449 + } 450 + 451 + if (addrlen != sizeof(sa)) { 452 + ret = -LIBBPF_ERRNO__INTERNAL; 453 + goto cleanup; 454 + } 455 + 456 + memset(&req, 0, sizeof(req)); 457 + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 458 + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 459 + req.nh.nlmsg_type = RTM_SETLINK; 460 + req.nh.nlmsg_pid = 0; 461 + req.nh.nlmsg_seq = ++seq; 462 + req.ifinfo.ifi_family = AF_UNSPEC; 463 + req.ifinfo.ifi_index = ifindex; 464 + 465 + /* started nested attribute for XDP */ 466 + nla = (struct nlattr *)(((char *)&req) 467 + + NLMSG_ALIGN(req.nh.nlmsg_len)); 468 + nla->nla_type = NLA_F_NESTED | IFLA_XDP; 469 + nla->nla_len = NLA_HDRLEN; 470 + 471 + /* add XDP fd */ 472 + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 473 + nla_xdp->nla_type = IFLA_XDP_FD; 474 + nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); 475 + memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); 476 + nla->nla_len += nla_xdp->nla_len; 477 + 478 + /* if user passed in any flags, add those too */ 479 + if (flags) { 480 + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); 481 + nla_xdp->nla_type = IFLA_XDP_FLAGS; 482 + nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); 483 + memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); 484 + nla->nla_len += nla_xdp->nla_len; 485 + } 486 + 487 + req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); 488 + 489 + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { 490 + ret = -errno; 491 + goto cleanup; 492 + } 493 + 494 + len = recv(sock, buf, sizeof(buf), 0); 495 + if (len < 0) { 496 + ret = -errno; 497 + goto cleanup; 498 + } 499 + 500 + for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 501 + nh = NLMSG_NEXT(nh, len)) { 502 + if (nh->nlmsg_pid != sa.nl_pid) { 503 + ret = -LIBBPF_ERRNO__WRNGPID; 504 + goto cleanup; 505 + } 506 + if (nh->nlmsg_seq != seq) { 507 + ret = -LIBBPF_ERRNO__INVSEQ; 508 + goto cleanup; 509 + } 510 + switch (nh->nlmsg_type) { 511 + case NLMSG_ERROR: 512 + err = (struct nlmsgerr *)NLMSG_DATA(nh); 513 + if (!err->error) 514 + continue; 515 + ret = err->error; 516 + goto cleanup; 517 + case NLMSG_DONE: 518 + break; 519 + default: 520 + break; 521 + } 522 + } 523 + 524 + ret = 0; 525 + 526 + cleanup: 527 + close(sock); 528 + return ret; 423 529 }
+2
tools/lib/bpf/libbpf.c
··· 106 106 [ERRCODE_OFFSET(PROG2BIG)] = "Program too big", 107 107 [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version", 108 108 [ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type", 109 + [ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message", 110 + [ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence", 109 111 }; 110 112 111 113 int libbpf_strerror(int err, char *buf, size_t size)
+4
tools/lib/bpf/libbpf.h
··· 42 42 LIBBPF_ERRNO__PROG2BIG, /* Program too big */ 43 43 LIBBPF_ERRNO__KVER, /* Incorrect kernel version */ 44 44 LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */ 45 + LIBBPF_ERRNO__WRNGPID, /* Wrong pid in netlink message */ 46 + LIBBPF_ERRNO__INVSEQ, /* Invalid netlink sequence */ 45 47 __LIBBPF_ERRNO__END, 46 48 }; 47 49 ··· 248 246 249 247 int bpf_prog_load(const char *file, enum bpf_prog_type type, 250 248 struct bpf_object **pobj, int *prog_fd); 249 + 250 + int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags); 251 251 #endif