17c2aad20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 27c2aad20Sopenharmony_ci/* Copyright (c) 2018 Facebook */ 37c2aad20Sopenharmony_ci 47c2aad20Sopenharmony_ci#include <stdlib.h> 57c2aad20Sopenharmony_ci#include <memory.h> 67c2aad20Sopenharmony_ci#include <unistd.h> 77c2aad20Sopenharmony_ci#include <arpa/inet.h> 87c2aad20Sopenharmony_ci#include <linux/bpf.h> 97c2aad20Sopenharmony_ci#include <linux/if_ether.h> 107c2aad20Sopenharmony_ci#include <linux/pkt_cls.h> 117c2aad20Sopenharmony_ci#include <linux/rtnetlink.h> 127c2aad20Sopenharmony_ci#include <linux/netdev.h> 137c2aad20Sopenharmony_ci#include <sys/socket.h> 147c2aad20Sopenharmony_ci#include <errno.h> 157c2aad20Sopenharmony_ci#include <time.h> 167c2aad20Sopenharmony_ci 177c2aad20Sopenharmony_ci#include "bpf.h" 187c2aad20Sopenharmony_ci#include "libbpf.h" 197c2aad20Sopenharmony_ci#include "libbpf_internal.h" 207c2aad20Sopenharmony_ci#include "nlattr.h" 217c2aad20Sopenharmony_ci 227c2aad20Sopenharmony_ci#ifndef SOL_NETLINK 237c2aad20Sopenharmony_ci#define SOL_NETLINK 270 247c2aad20Sopenharmony_ci#endif 257c2aad20Sopenharmony_ci 267c2aad20Sopenharmony_citypedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 277c2aad20Sopenharmony_ci 287c2aad20Sopenharmony_citypedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 297c2aad20Sopenharmony_ci void *cookie); 307c2aad20Sopenharmony_ci 317c2aad20Sopenharmony_cistruct xdp_link_info { 327c2aad20Sopenharmony_ci __u32 prog_id; 337c2aad20Sopenharmony_ci __u32 drv_prog_id; 347c2aad20Sopenharmony_ci __u32 hw_prog_id; 357c2aad20Sopenharmony_ci __u32 skb_prog_id; 367c2aad20Sopenharmony_ci __u8 attach_mode; 377c2aad20Sopenharmony_ci}; 387c2aad20Sopenharmony_ci 397c2aad20Sopenharmony_cistruct xdp_id_md { 407c2aad20Sopenharmony_ci int ifindex; 417c2aad20Sopenharmony_ci __u32 flags; 427c2aad20Sopenharmony_ci struct xdp_link_info info; 437c2aad20Sopenharmony_ci __u64 feature_flags; 447c2aad20Sopenharmony_ci}; 457c2aad20Sopenharmony_ci 467c2aad20Sopenharmony_cistruct xdp_features_md { 477c2aad20Sopenharmony_ci int ifindex; 487c2aad20Sopenharmony_ci __u32 xdp_zc_max_segs; 497c2aad20Sopenharmony_ci __u64 flags; 507c2aad20Sopenharmony_ci}; 517c2aad20Sopenharmony_ci 527c2aad20Sopenharmony_cistatic int libbpf_netlink_open(__u32 *nl_pid, int proto) 537c2aad20Sopenharmony_ci{ 547c2aad20Sopenharmony_ci struct sockaddr_nl sa; 557c2aad20Sopenharmony_ci socklen_t addrlen; 567c2aad20Sopenharmony_ci int one = 1, ret; 577c2aad20Sopenharmony_ci int sock; 587c2aad20Sopenharmony_ci 597c2aad20Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 607c2aad20Sopenharmony_ci sa.nl_family = AF_NETLINK; 617c2aad20Sopenharmony_ci 627c2aad20Sopenharmony_ci sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto); 637c2aad20Sopenharmony_ci if (sock < 0) 647c2aad20Sopenharmony_ci return -errno; 657c2aad20Sopenharmony_ci 667c2aad20Sopenharmony_ci if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 677c2aad20Sopenharmony_ci &one, sizeof(one)) < 0) { 687c2aad20Sopenharmony_ci pr_warn("Netlink error reporting not supported\n"); 697c2aad20Sopenharmony_ci } 707c2aad20Sopenharmony_ci 717c2aad20Sopenharmony_ci if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 727c2aad20Sopenharmony_ci ret = -errno; 737c2aad20Sopenharmony_ci goto cleanup; 747c2aad20Sopenharmony_ci } 757c2aad20Sopenharmony_ci 767c2aad20Sopenharmony_ci addrlen = sizeof(sa); 777c2aad20Sopenharmony_ci if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 787c2aad20Sopenharmony_ci ret = -errno; 797c2aad20Sopenharmony_ci goto cleanup; 807c2aad20Sopenharmony_ci } 817c2aad20Sopenharmony_ci 827c2aad20Sopenharmony_ci if (addrlen != sizeof(sa)) { 837c2aad20Sopenharmony_ci ret = -LIBBPF_ERRNO__INTERNAL; 847c2aad20Sopenharmony_ci goto cleanup; 857c2aad20Sopenharmony_ci } 867c2aad20Sopenharmony_ci 877c2aad20Sopenharmony_ci *nl_pid = sa.nl_pid; 887c2aad20Sopenharmony_ci return sock; 897c2aad20Sopenharmony_ci 907c2aad20Sopenharmony_cicleanup: 917c2aad20Sopenharmony_ci close(sock); 927c2aad20Sopenharmony_ci return ret; 937c2aad20Sopenharmony_ci} 947c2aad20Sopenharmony_ci 957c2aad20Sopenharmony_cistatic void libbpf_netlink_close(int sock) 967c2aad20Sopenharmony_ci{ 977c2aad20Sopenharmony_ci close(sock); 987c2aad20Sopenharmony_ci} 997c2aad20Sopenharmony_ci 1007c2aad20Sopenharmony_cienum { 1017c2aad20Sopenharmony_ci NL_CONT, 1027c2aad20Sopenharmony_ci NL_NEXT, 1037c2aad20Sopenharmony_ci NL_DONE, 1047c2aad20Sopenharmony_ci}; 1057c2aad20Sopenharmony_ci 1067c2aad20Sopenharmony_cistatic int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags) 1077c2aad20Sopenharmony_ci{ 1087c2aad20Sopenharmony_ci int len; 1097c2aad20Sopenharmony_ci 1107c2aad20Sopenharmony_ci do { 1117c2aad20Sopenharmony_ci len = recvmsg(sock, mhdr, flags); 1127c2aad20Sopenharmony_ci } while (len < 0 && (errno == EINTR || errno == EAGAIN)); 1137c2aad20Sopenharmony_ci 1147c2aad20Sopenharmony_ci if (len < 0) 1157c2aad20Sopenharmony_ci return -errno; 1167c2aad20Sopenharmony_ci return len; 1177c2aad20Sopenharmony_ci} 1187c2aad20Sopenharmony_ci 1197c2aad20Sopenharmony_cistatic int alloc_iov(struct iovec *iov, int len) 1207c2aad20Sopenharmony_ci{ 1217c2aad20Sopenharmony_ci void *nbuf; 1227c2aad20Sopenharmony_ci 1237c2aad20Sopenharmony_ci nbuf = realloc(iov->iov_base, len); 1247c2aad20Sopenharmony_ci if (!nbuf) 1257c2aad20Sopenharmony_ci return -ENOMEM; 1267c2aad20Sopenharmony_ci 1277c2aad20Sopenharmony_ci iov->iov_base = nbuf; 1287c2aad20Sopenharmony_ci iov->iov_len = len; 1297c2aad20Sopenharmony_ci return 0; 1307c2aad20Sopenharmony_ci} 1317c2aad20Sopenharmony_ci 1327c2aad20Sopenharmony_cistatic int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, 1337c2aad20Sopenharmony_ci __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 1347c2aad20Sopenharmony_ci void *cookie) 1357c2aad20Sopenharmony_ci{ 1367c2aad20Sopenharmony_ci struct iovec iov = {}; 1377c2aad20Sopenharmony_ci struct msghdr mhdr = { 1387c2aad20Sopenharmony_ci .msg_iov = &iov, 1397c2aad20Sopenharmony_ci .msg_iovlen = 1, 1407c2aad20Sopenharmony_ci }; 1417c2aad20Sopenharmony_ci bool multipart = true; 1427c2aad20Sopenharmony_ci struct nlmsgerr *err; 1437c2aad20Sopenharmony_ci struct nlmsghdr *nh; 1447c2aad20Sopenharmony_ci int len, ret; 1457c2aad20Sopenharmony_ci 1467c2aad20Sopenharmony_ci ret = alloc_iov(&iov, 4096); 1477c2aad20Sopenharmony_ci if (ret) 1487c2aad20Sopenharmony_ci goto done; 1497c2aad20Sopenharmony_ci 1507c2aad20Sopenharmony_ci while (multipart) { 1517c2aad20Sopenharmony_cistart: 1527c2aad20Sopenharmony_ci multipart = false; 1537c2aad20Sopenharmony_ci len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC); 1547c2aad20Sopenharmony_ci if (len < 0) { 1557c2aad20Sopenharmony_ci ret = len; 1567c2aad20Sopenharmony_ci goto done; 1577c2aad20Sopenharmony_ci } 1587c2aad20Sopenharmony_ci 1597c2aad20Sopenharmony_ci if (len > iov.iov_len) { 1607c2aad20Sopenharmony_ci ret = alloc_iov(&iov, len); 1617c2aad20Sopenharmony_ci if (ret) 1627c2aad20Sopenharmony_ci goto done; 1637c2aad20Sopenharmony_ci } 1647c2aad20Sopenharmony_ci 1657c2aad20Sopenharmony_ci len = netlink_recvmsg(sock, &mhdr, 0); 1667c2aad20Sopenharmony_ci if (len < 0) { 1677c2aad20Sopenharmony_ci ret = len; 1687c2aad20Sopenharmony_ci goto done; 1697c2aad20Sopenharmony_ci } 1707c2aad20Sopenharmony_ci 1717c2aad20Sopenharmony_ci if (len == 0) 1727c2aad20Sopenharmony_ci break; 1737c2aad20Sopenharmony_ci 1747c2aad20Sopenharmony_ci for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len); 1757c2aad20Sopenharmony_ci nh = NLMSG_NEXT(nh, len)) { 1767c2aad20Sopenharmony_ci if (nh->nlmsg_pid != nl_pid) { 1777c2aad20Sopenharmony_ci ret = -LIBBPF_ERRNO__WRNGPID; 1787c2aad20Sopenharmony_ci goto done; 1797c2aad20Sopenharmony_ci } 1807c2aad20Sopenharmony_ci if (nh->nlmsg_seq != seq) { 1817c2aad20Sopenharmony_ci ret = -LIBBPF_ERRNO__INVSEQ; 1827c2aad20Sopenharmony_ci goto done; 1837c2aad20Sopenharmony_ci } 1847c2aad20Sopenharmony_ci if (nh->nlmsg_flags & NLM_F_MULTI) 1857c2aad20Sopenharmony_ci multipart = true; 1867c2aad20Sopenharmony_ci switch (nh->nlmsg_type) { 1877c2aad20Sopenharmony_ci case NLMSG_ERROR: 1887c2aad20Sopenharmony_ci err = (struct nlmsgerr *)NLMSG_DATA(nh); 1897c2aad20Sopenharmony_ci if (!err->error) 1907c2aad20Sopenharmony_ci continue; 1917c2aad20Sopenharmony_ci ret = err->error; 1927c2aad20Sopenharmony_ci libbpf_nla_dump_errormsg(nh); 1937c2aad20Sopenharmony_ci goto done; 1947c2aad20Sopenharmony_ci case NLMSG_DONE: 1957c2aad20Sopenharmony_ci ret = 0; 1967c2aad20Sopenharmony_ci goto done; 1977c2aad20Sopenharmony_ci default: 1987c2aad20Sopenharmony_ci break; 1997c2aad20Sopenharmony_ci } 2007c2aad20Sopenharmony_ci if (_fn) { 2017c2aad20Sopenharmony_ci ret = _fn(nh, fn, cookie); 2027c2aad20Sopenharmony_ci switch (ret) { 2037c2aad20Sopenharmony_ci case NL_CONT: 2047c2aad20Sopenharmony_ci break; 2057c2aad20Sopenharmony_ci case NL_NEXT: 2067c2aad20Sopenharmony_ci goto start; 2077c2aad20Sopenharmony_ci case NL_DONE: 2087c2aad20Sopenharmony_ci ret = 0; 2097c2aad20Sopenharmony_ci goto done; 2107c2aad20Sopenharmony_ci default: 2117c2aad20Sopenharmony_ci goto done; 2127c2aad20Sopenharmony_ci } 2137c2aad20Sopenharmony_ci } 2147c2aad20Sopenharmony_ci } 2157c2aad20Sopenharmony_ci } 2167c2aad20Sopenharmony_ci ret = 0; 2177c2aad20Sopenharmony_cidone: 2187c2aad20Sopenharmony_ci free(iov.iov_base); 2197c2aad20Sopenharmony_ci return ret; 2207c2aad20Sopenharmony_ci} 2217c2aad20Sopenharmony_ci 2227c2aad20Sopenharmony_cistatic int libbpf_netlink_send_recv(struct libbpf_nla_req *req, 2237c2aad20Sopenharmony_ci int proto, __dump_nlmsg_t parse_msg, 2247c2aad20Sopenharmony_ci libbpf_dump_nlmsg_t parse_attr, 2257c2aad20Sopenharmony_ci void *cookie) 2267c2aad20Sopenharmony_ci{ 2277c2aad20Sopenharmony_ci __u32 nl_pid = 0; 2287c2aad20Sopenharmony_ci int sock, ret; 2297c2aad20Sopenharmony_ci 2307c2aad20Sopenharmony_ci sock = libbpf_netlink_open(&nl_pid, proto); 2317c2aad20Sopenharmony_ci if (sock < 0) 2327c2aad20Sopenharmony_ci return sock; 2337c2aad20Sopenharmony_ci 2347c2aad20Sopenharmony_ci req->nh.nlmsg_pid = 0; 2357c2aad20Sopenharmony_ci req->nh.nlmsg_seq = time(NULL); 2367c2aad20Sopenharmony_ci 2377c2aad20Sopenharmony_ci if (send(sock, req, req->nh.nlmsg_len, 0) < 0) { 2387c2aad20Sopenharmony_ci ret = -errno; 2397c2aad20Sopenharmony_ci goto out; 2407c2aad20Sopenharmony_ci } 2417c2aad20Sopenharmony_ci 2427c2aad20Sopenharmony_ci ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq, 2437c2aad20Sopenharmony_ci parse_msg, parse_attr, cookie); 2447c2aad20Sopenharmony_ciout: 2457c2aad20Sopenharmony_ci libbpf_netlink_close(sock); 2467c2aad20Sopenharmony_ci return ret; 2477c2aad20Sopenharmony_ci} 2487c2aad20Sopenharmony_ci 2497c2aad20Sopenharmony_cistatic int parse_genl_family_id(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 2507c2aad20Sopenharmony_ci void *cookie) 2517c2aad20Sopenharmony_ci{ 2527c2aad20Sopenharmony_ci struct genlmsghdr *gnl = NLMSG_DATA(nh); 2537c2aad20Sopenharmony_ci struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN); 2547c2aad20Sopenharmony_ci struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1]; 2557c2aad20Sopenharmony_ci __u16 *id = cookie; 2567c2aad20Sopenharmony_ci 2577c2aad20Sopenharmony_ci libbpf_nla_parse(tb, CTRL_ATTR_FAMILY_ID, na, 2587c2aad20Sopenharmony_ci NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL); 2597c2aad20Sopenharmony_ci if (!tb[CTRL_ATTR_FAMILY_ID]) 2607c2aad20Sopenharmony_ci return NL_CONT; 2617c2aad20Sopenharmony_ci 2627c2aad20Sopenharmony_ci *id = libbpf_nla_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]); 2637c2aad20Sopenharmony_ci return NL_DONE; 2647c2aad20Sopenharmony_ci} 2657c2aad20Sopenharmony_ci 2667c2aad20Sopenharmony_cistatic int libbpf_netlink_resolve_genl_family_id(const char *name, 2677c2aad20Sopenharmony_ci __u16 len, __u16 *id) 2687c2aad20Sopenharmony_ci{ 2697c2aad20Sopenharmony_ci struct libbpf_nla_req req = { 2707c2aad20Sopenharmony_ci .nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN), 2717c2aad20Sopenharmony_ci .nh.nlmsg_type = GENL_ID_CTRL, 2727c2aad20Sopenharmony_ci .nh.nlmsg_flags = NLM_F_REQUEST, 2737c2aad20Sopenharmony_ci .gnl.cmd = CTRL_CMD_GETFAMILY, 2747c2aad20Sopenharmony_ci .gnl.version = 2, 2757c2aad20Sopenharmony_ci }; 2767c2aad20Sopenharmony_ci int err; 2777c2aad20Sopenharmony_ci 2787c2aad20Sopenharmony_ci err = nlattr_add(&req, CTRL_ATTR_FAMILY_NAME, name, len); 2797c2aad20Sopenharmony_ci if (err < 0) 2807c2aad20Sopenharmony_ci return err; 2817c2aad20Sopenharmony_ci 2827c2aad20Sopenharmony_ci return libbpf_netlink_send_recv(&req, NETLINK_GENERIC, 2837c2aad20Sopenharmony_ci parse_genl_family_id, NULL, id); 2847c2aad20Sopenharmony_ci} 2857c2aad20Sopenharmony_ci 2867c2aad20Sopenharmony_cistatic int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, 2877c2aad20Sopenharmony_ci __u32 flags) 2887c2aad20Sopenharmony_ci{ 2897c2aad20Sopenharmony_ci struct nlattr *nla; 2907c2aad20Sopenharmony_ci int ret; 2917c2aad20Sopenharmony_ci struct libbpf_nla_req req; 2927c2aad20Sopenharmony_ci 2937c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 2947c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 2957c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 2967c2aad20Sopenharmony_ci req.nh.nlmsg_type = RTM_SETLINK; 2977c2aad20Sopenharmony_ci req.ifinfo.ifi_family = AF_UNSPEC; 2987c2aad20Sopenharmony_ci req.ifinfo.ifi_index = ifindex; 2997c2aad20Sopenharmony_ci 3007c2aad20Sopenharmony_ci nla = nlattr_begin_nested(&req, IFLA_XDP); 3017c2aad20Sopenharmony_ci if (!nla) 3027c2aad20Sopenharmony_ci return -EMSGSIZE; 3037c2aad20Sopenharmony_ci ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd)); 3047c2aad20Sopenharmony_ci if (ret < 0) 3057c2aad20Sopenharmony_ci return ret; 3067c2aad20Sopenharmony_ci if (flags) { 3077c2aad20Sopenharmony_ci ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags)); 3087c2aad20Sopenharmony_ci if (ret < 0) 3097c2aad20Sopenharmony_ci return ret; 3107c2aad20Sopenharmony_ci } 3117c2aad20Sopenharmony_ci if (flags & XDP_FLAGS_REPLACE) { 3127c2aad20Sopenharmony_ci ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd, 3137c2aad20Sopenharmony_ci sizeof(old_fd)); 3147c2aad20Sopenharmony_ci if (ret < 0) 3157c2aad20Sopenharmony_ci return ret; 3167c2aad20Sopenharmony_ci } 3177c2aad20Sopenharmony_ci nlattr_end_nested(&req, nla); 3187c2aad20Sopenharmony_ci 3197c2aad20Sopenharmony_ci return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL); 3207c2aad20Sopenharmony_ci} 3217c2aad20Sopenharmony_ci 3227c2aad20Sopenharmony_ciint bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts) 3237c2aad20Sopenharmony_ci{ 3247c2aad20Sopenharmony_ci int old_prog_fd, err; 3257c2aad20Sopenharmony_ci 3267c2aad20Sopenharmony_ci if (!OPTS_VALID(opts, bpf_xdp_attach_opts)) 3277c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 3287c2aad20Sopenharmony_ci 3297c2aad20Sopenharmony_ci old_prog_fd = OPTS_GET(opts, old_prog_fd, 0); 3307c2aad20Sopenharmony_ci if (old_prog_fd) 3317c2aad20Sopenharmony_ci flags |= XDP_FLAGS_REPLACE; 3327c2aad20Sopenharmony_ci else 3337c2aad20Sopenharmony_ci old_prog_fd = -1; 3347c2aad20Sopenharmony_ci 3357c2aad20Sopenharmony_ci err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags); 3367c2aad20Sopenharmony_ci return libbpf_err(err); 3377c2aad20Sopenharmony_ci} 3387c2aad20Sopenharmony_ci 3397c2aad20Sopenharmony_ciint bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts) 3407c2aad20Sopenharmony_ci{ 3417c2aad20Sopenharmony_ci return bpf_xdp_attach(ifindex, -1, flags, opts); 3427c2aad20Sopenharmony_ci} 3437c2aad20Sopenharmony_ci 3447c2aad20Sopenharmony_cistatic int __dump_link_nlmsg(struct nlmsghdr *nlh, 3457c2aad20Sopenharmony_ci libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 3467c2aad20Sopenharmony_ci{ 3477c2aad20Sopenharmony_ci struct nlattr *tb[IFLA_MAX + 1], *attr; 3487c2aad20Sopenharmony_ci struct ifinfomsg *ifi = NLMSG_DATA(nlh); 3497c2aad20Sopenharmony_ci int len; 3507c2aad20Sopenharmony_ci 3517c2aad20Sopenharmony_ci len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 3527c2aad20Sopenharmony_ci attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 3537c2aad20Sopenharmony_ci 3547c2aad20Sopenharmony_ci if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 3557c2aad20Sopenharmony_ci return -LIBBPF_ERRNO__NLPARSE; 3567c2aad20Sopenharmony_ci 3577c2aad20Sopenharmony_ci return dump_link_nlmsg(cookie, ifi, tb); 3587c2aad20Sopenharmony_ci} 3597c2aad20Sopenharmony_ci 3607c2aad20Sopenharmony_cistatic int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) 3617c2aad20Sopenharmony_ci{ 3627c2aad20Sopenharmony_ci struct nlattr *xdp_tb[IFLA_XDP_MAX + 1]; 3637c2aad20Sopenharmony_ci struct xdp_id_md *xdp_id = cookie; 3647c2aad20Sopenharmony_ci struct ifinfomsg *ifinfo = msg; 3657c2aad20Sopenharmony_ci int ret; 3667c2aad20Sopenharmony_ci 3677c2aad20Sopenharmony_ci if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index) 3687c2aad20Sopenharmony_ci return 0; 3697c2aad20Sopenharmony_ci 3707c2aad20Sopenharmony_ci if (!tb[IFLA_XDP]) 3717c2aad20Sopenharmony_ci return 0; 3727c2aad20Sopenharmony_ci 3737c2aad20Sopenharmony_ci ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL); 3747c2aad20Sopenharmony_ci if (ret) 3757c2aad20Sopenharmony_ci return ret; 3767c2aad20Sopenharmony_ci 3777c2aad20Sopenharmony_ci if (!xdp_tb[IFLA_XDP_ATTACHED]) 3787c2aad20Sopenharmony_ci return 0; 3797c2aad20Sopenharmony_ci 3807c2aad20Sopenharmony_ci xdp_id->info.attach_mode = libbpf_nla_getattr_u8( 3817c2aad20Sopenharmony_ci xdp_tb[IFLA_XDP_ATTACHED]); 3827c2aad20Sopenharmony_ci 3837c2aad20Sopenharmony_ci if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE) 3847c2aad20Sopenharmony_ci return 0; 3857c2aad20Sopenharmony_ci 3867c2aad20Sopenharmony_ci if (xdp_tb[IFLA_XDP_PROG_ID]) 3877c2aad20Sopenharmony_ci xdp_id->info.prog_id = libbpf_nla_getattr_u32( 3887c2aad20Sopenharmony_ci xdp_tb[IFLA_XDP_PROG_ID]); 3897c2aad20Sopenharmony_ci 3907c2aad20Sopenharmony_ci if (xdp_tb[IFLA_XDP_SKB_PROG_ID]) 3917c2aad20Sopenharmony_ci xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32( 3927c2aad20Sopenharmony_ci xdp_tb[IFLA_XDP_SKB_PROG_ID]); 3937c2aad20Sopenharmony_ci 3947c2aad20Sopenharmony_ci if (xdp_tb[IFLA_XDP_DRV_PROG_ID]) 3957c2aad20Sopenharmony_ci xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32( 3967c2aad20Sopenharmony_ci xdp_tb[IFLA_XDP_DRV_PROG_ID]); 3977c2aad20Sopenharmony_ci 3987c2aad20Sopenharmony_ci if (xdp_tb[IFLA_XDP_HW_PROG_ID]) 3997c2aad20Sopenharmony_ci xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32( 4007c2aad20Sopenharmony_ci xdp_tb[IFLA_XDP_HW_PROG_ID]); 4017c2aad20Sopenharmony_ci 4027c2aad20Sopenharmony_ci return 0; 4037c2aad20Sopenharmony_ci} 4047c2aad20Sopenharmony_ci 4057c2aad20Sopenharmony_cistatic int parse_xdp_features(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 4067c2aad20Sopenharmony_ci void *cookie) 4077c2aad20Sopenharmony_ci{ 4087c2aad20Sopenharmony_ci struct genlmsghdr *gnl = NLMSG_DATA(nh); 4097c2aad20Sopenharmony_ci struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN); 4107c2aad20Sopenharmony_ci struct nlattr *tb[NETDEV_CMD_MAX + 1]; 4117c2aad20Sopenharmony_ci struct xdp_features_md *md = cookie; 4127c2aad20Sopenharmony_ci __u32 ifindex; 4137c2aad20Sopenharmony_ci 4147c2aad20Sopenharmony_ci libbpf_nla_parse(tb, NETDEV_CMD_MAX, na, 4157c2aad20Sopenharmony_ci NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL); 4167c2aad20Sopenharmony_ci 4177c2aad20Sopenharmony_ci if (!tb[NETDEV_A_DEV_IFINDEX] || !tb[NETDEV_A_DEV_XDP_FEATURES]) 4187c2aad20Sopenharmony_ci return NL_CONT; 4197c2aad20Sopenharmony_ci 4207c2aad20Sopenharmony_ci ifindex = libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_IFINDEX]); 4217c2aad20Sopenharmony_ci if (ifindex != md->ifindex) 4227c2aad20Sopenharmony_ci return NL_CONT; 4237c2aad20Sopenharmony_ci 4247c2aad20Sopenharmony_ci md->flags = libbpf_nla_getattr_u64(tb[NETDEV_A_DEV_XDP_FEATURES]); 4257c2aad20Sopenharmony_ci if (tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS]) 4267c2aad20Sopenharmony_ci md->xdp_zc_max_segs = 4277c2aad20Sopenharmony_ci libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS]); 4287c2aad20Sopenharmony_ci return NL_DONE; 4297c2aad20Sopenharmony_ci} 4307c2aad20Sopenharmony_ci 4317c2aad20Sopenharmony_ciint bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts) 4327c2aad20Sopenharmony_ci{ 4337c2aad20Sopenharmony_ci struct libbpf_nla_req req = { 4347c2aad20Sopenharmony_ci .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 4357c2aad20Sopenharmony_ci .nh.nlmsg_type = RTM_GETLINK, 4367c2aad20Sopenharmony_ci .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 4377c2aad20Sopenharmony_ci .ifinfo.ifi_family = AF_PACKET, 4387c2aad20Sopenharmony_ci }; 4397c2aad20Sopenharmony_ci struct xdp_id_md xdp_id = {}; 4407c2aad20Sopenharmony_ci struct xdp_features_md md = { 4417c2aad20Sopenharmony_ci .ifindex = ifindex, 4427c2aad20Sopenharmony_ci }; 4437c2aad20Sopenharmony_ci __u16 id; 4447c2aad20Sopenharmony_ci int err; 4457c2aad20Sopenharmony_ci 4467c2aad20Sopenharmony_ci if (!OPTS_VALID(opts, bpf_xdp_query_opts)) 4477c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 4487c2aad20Sopenharmony_ci 4497c2aad20Sopenharmony_ci if (xdp_flags & ~XDP_FLAGS_MASK) 4507c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 4517c2aad20Sopenharmony_ci 4527c2aad20Sopenharmony_ci /* Check whether the single {HW,DRV,SKB} mode is set */ 4537c2aad20Sopenharmony_ci xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE; 4547c2aad20Sopenharmony_ci if (xdp_flags & (xdp_flags - 1)) 4557c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 4567c2aad20Sopenharmony_ci 4577c2aad20Sopenharmony_ci xdp_id.ifindex = ifindex; 4587c2aad20Sopenharmony_ci xdp_id.flags = xdp_flags; 4597c2aad20Sopenharmony_ci 4607c2aad20Sopenharmony_ci err = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, __dump_link_nlmsg, 4617c2aad20Sopenharmony_ci get_xdp_info, &xdp_id); 4627c2aad20Sopenharmony_ci if (err) 4637c2aad20Sopenharmony_ci return libbpf_err(err); 4647c2aad20Sopenharmony_ci 4657c2aad20Sopenharmony_ci OPTS_SET(opts, prog_id, xdp_id.info.prog_id); 4667c2aad20Sopenharmony_ci OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id); 4677c2aad20Sopenharmony_ci OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id); 4687c2aad20Sopenharmony_ci OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id); 4697c2aad20Sopenharmony_ci OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode); 4707c2aad20Sopenharmony_ci 4717c2aad20Sopenharmony_ci if (!OPTS_HAS(opts, feature_flags)) 4727c2aad20Sopenharmony_ci return 0; 4737c2aad20Sopenharmony_ci 4747c2aad20Sopenharmony_ci err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id); 4757c2aad20Sopenharmony_ci if (err < 0) { 4767c2aad20Sopenharmony_ci if (err == -ENOENT) { 4777c2aad20Sopenharmony_ci opts->feature_flags = 0; 4787c2aad20Sopenharmony_ci goto skip_feature_flags; 4797c2aad20Sopenharmony_ci } 4807c2aad20Sopenharmony_ci return libbpf_err(err); 4817c2aad20Sopenharmony_ci } 4827c2aad20Sopenharmony_ci 4837c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 4847c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 4857c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST; 4867c2aad20Sopenharmony_ci req.nh.nlmsg_type = id; 4877c2aad20Sopenharmony_ci req.gnl.cmd = NETDEV_CMD_DEV_GET; 4887c2aad20Sopenharmony_ci req.gnl.version = 2; 4897c2aad20Sopenharmony_ci 4907c2aad20Sopenharmony_ci err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex)); 4917c2aad20Sopenharmony_ci if (err < 0) 4927c2aad20Sopenharmony_ci return libbpf_err(err); 4937c2aad20Sopenharmony_ci 4947c2aad20Sopenharmony_ci err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC, 4957c2aad20Sopenharmony_ci parse_xdp_features, NULL, &md); 4967c2aad20Sopenharmony_ci if (err) 4977c2aad20Sopenharmony_ci return libbpf_err(err); 4987c2aad20Sopenharmony_ci 4997c2aad20Sopenharmony_ci opts->feature_flags = md.flags; 5007c2aad20Sopenharmony_ci opts->xdp_zc_max_segs = md.xdp_zc_max_segs; 5017c2aad20Sopenharmony_ci 5027c2aad20Sopenharmony_ciskip_feature_flags: 5037c2aad20Sopenharmony_ci return 0; 5047c2aad20Sopenharmony_ci} 5057c2aad20Sopenharmony_ci 5067c2aad20Sopenharmony_ciint bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id) 5077c2aad20Sopenharmony_ci{ 5087c2aad20Sopenharmony_ci LIBBPF_OPTS(bpf_xdp_query_opts, opts); 5097c2aad20Sopenharmony_ci int ret; 5107c2aad20Sopenharmony_ci 5117c2aad20Sopenharmony_ci ret = bpf_xdp_query(ifindex, flags, &opts); 5127c2aad20Sopenharmony_ci if (ret) 5137c2aad20Sopenharmony_ci return libbpf_err(ret); 5147c2aad20Sopenharmony_ci 5157c2aad20Sopenharmony_ci flags &= XDP_FLAGS_MODES; 5167c2aad20Sopenharmony_ci 5177c2aad20Sopenharmony_ci if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags) 5187c2aad20Sopenharmony_ci *prog_id = opts.prog_id; 5197c2aad20Sopenharmony_ci else if (flags & XDP_FLAGS_DRV_MODE) 5207c2aad20Sopenharmony_ci *prog_id = opts.drv_prog_id; 5217c2aad20Sopenharmony_ci else if (flags & XDP_FLAGS_HW_MODE) 5227c2aad20Sopenharmony_ci *prog_id = opts.hw_prog_id; 5237c2aad20Sopenharmony_ci else if (flags & XDP_FLAGS_SKB_MODE) 5247c2aad20Sopenharmony_ci *prog_id = opts.skb_prog_id; 5257c2aad20Sopenharmony_ci else 5267c2aad20Sopenharmony_ci *prog_id = 0; 5277c2aad20Sopenharmony_ci 5287c2aad20Sopenharmony_ci return 0; 5297c2aad20Sopenharmony_ci} 5307c2aad20Sopenharmony_ci 5317c2aad20Sopenharmony_ci 5327c2aad20Sopenharmony_citypedef int (*qdisc_config_t)(struct libbpf_nla_req *req); 5337c2aad20Sopenharmony_ci 5347c2aad20Sopenharmony_cistatic int clsact_config(struct libbpf_nla_req *req) 5357c2aad20Sopenharmony_ci{ 5367c2aad20Sopenharmony_ci req->tc.tcm_parent = TC_H_CLSACT; 5377c2aad20Sopenharmony_ci req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); 5387c2aad20Sopenharmony_ci 5397c2aad20Sopenharmony_ci return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact")); 5407c2aad20Sopenharmony_ci} 5417c2aad20Sopenharmony_ci 5427c2aad20Sopenharmony_cistatic int attach_point_to_config(struct bpf_tc_hook *hook, 5437c2aad20Sopenharmony_ci qdisc_config_t *config) 5447c2aad20Sopenharmony_ci{ 5457c2aad20Sopenharmony_ci switch (OPTS_GET(hook, attach_point, 0)) { 5467c2aad20Sopenharmony_ci case BPF_TC_INGRESS: 5477c2aad20Sopenharmony_ci case BPF_TC_EGRESS: 5487c2aad20Sopenharmony_ci case BPF_TC_INGRESS | BPF_TC_EGRESS: 5497c2aad20Sopenharmony_ci if (OPTS_GET(hook, parent, 0)) 5507c2aad20Sopenharmony_ci return -EINVAL; 5517c2aad20Sopenharmony_ci *config = &clsact_config; 5527c2aad20Sopenharmony_ci return 0; 5537c2aad20Sopenharmony_ci case BPF_TC_CUSTOM: 5547c2aad20Sopenharmony_ci return -EOPNOTSUPP; 5557c2aad20Sopenharmony_ci default: 5567c2aad20Sopenharmony_ci return -EINVAL; 5577c2aad20Sopenharmony_ci } 5587c2aad20Sopenharmony_ci} 5597c2aad20Sopenharmony_ci 5607c2aad20Sopenharmony_cistatic int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point, 5617c2aad20Sopenharmony_ci __u32 *parent) 5627c2aad20Sopenharmony_ci{ 5637c2aad20Sopenharmony_ci switch (attach_point) { 5647c2aad20Sopenharmony_ci case BPF_TC_INGRESS: 5657c2aad20Sopenharmony_ci case BPF_TC_EGRESS: 5667c2aad20Sopenharmony_ci if (*parent) 5677c2aad20Sopenharmony_ci return -EINVAL; 5687c2aad20Sopenharmony_ci *parent = TC_H_MAKE(TC_H_CLSACT, 5697c2aad20Sopenharmony_ci attach_point == BPF_TC_INGRESS ? 5707c2aad20Sopenharmony_ci TC_H_MIN_INGRESS : TC_H_MIN_EGRESS); 5717c2aad20Sopenharmony_ci break; 5727c2aad20Sopenharmony_ci case BPF_TC_CUSTOM: 5737c2aad20Sopenharmony_ci if (!*parent) 5747c2aad20Sopenharmony_ci return -EINVAL; 5757c2aad20Sopenharmony_ci break; 5767c2aad20Sopenharmony_ci default: 5777c2aad20Sopenharmony_ci return -EINVAL; 5787c2aad20Sopenharmony_ci } 5797c2aad20Sopenharmony_ci return 0; 5807c2aad20Sopenharmony_ci} 5817c2aad20Sopenharmony_ci 5827c2aad20Sopenharmony_cistatic int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags) 5837c2aad20Sopenharmony_ci{ 5847c2aad20Sopenharmony_ci qdisc_config_t config; 5857c2aad20Sopenharmony_ci int ret; 5867c2aad20Sopenharmony_ci struct libbpf_nla_req req; 5877c2aad20Sopenharmony_ci 5887c2aad20Sopenharmony_ci ret = attach_point_to_config(hook, &config); 5897c2aad20Sopenharmony_ci if (ret < 0) 5907c2aad20Sopenharmony_ci return ret; 5917c2aad20Sopenharmony_ci 5927c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 5937c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 5947c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; 5957c2aad20Sopenharmony_ci req.nh.nlmsg_type = cmd; 5967c2aad20Sopenharmony_ci req.tc.tcm_family = AF_UNSPEC; 5977c2aad20Sopenharmony_ci req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0); 5987c2aad20Sopenharmony_ci 5997c2aad20Sopenharmony_ci ret = config(&req); 6007c2aad20Sopenharmony_ci if (ret < 0) 6017c2aad20Sopenharmony_ci return ret; 6027c2aad20Sopenharmony_ci 6037c2aad20Sopenharmony_ci return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL); 6047c2aad20Sopenharmony_ci} 6057c2aad20Sopenharmony_ci 6067c2aad20Sopenharmony_cistatic int tc_qdisc_create_excl(struct bpf_tc_hook *hook) 6077c2aad20Sopenharmony_ci{ 6087c2aad20Sopenharmony_ci return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL); 6097c2aad20Sopenharmony_ci} 6107c2aad20Sopenharmony_ci 6117c2aad20Sopenharmony_cistatic int tc_qdisc_delete(struct bpf_tc_hook *hook) 6127c2aad20Sopenharmony_ci{ 6137c2aad20Sopenharmony_ci return tc_qdisc_modify(hook, RTM_DELQDISC, 0); 6147c2aad20Sopenharmony_ci} 6157c2aad20Sopenharmony_ci 6167c2aad20Sopenharmony_ciint bpf_tc_hook_create(struct bpf_tc_hook *hook) 6177c2aad20Sopenharmony_ci{ 6187c2aad20Sopenharmony_ci int ret; 6197c2aad20Sopenharmony_ci 6207c2aad20Sopenharmony_ci if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 6217c2aad20Sopenharmony_ci OPTS_GET(hook, ifindex, 0) <= 0) 6227c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 6237c2aad20Sopenharmony_ci 6247c2aad20Sopenharmony_ci ret = tc_qdisc_create_excl(hook); 6257c2aad20Sopenharmony_ci return libbpf_err(ret); 6267c2aad20Sopenharmony_ci} 6277c2aad20Sopenharmony_ci 6287c2aad20Sopenharmony_cistatic int __bpf_tc_detach(const struct bpf_tc_hook *hook, 6297c2aad20Sopenharmony_ci const struct bpf_tc_opts *opts, 6307c2aad20Sopenharmony_ci const bool flush); 6317c2aad20Sopenharmony_ci 6327c2aad20Sopenharmony_ciint bpf_tc_hook_destroy(struct bpf_tc_hook *hook) 6337c2aad20Sopenharmony_ci{ 6347c2aad20Sopenharmony_ci if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 6357c2aad20Sopenharmony_ci OPTS_GET(hook, ifindex, 0) <= 0) 6367c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 6377c2aad20Sopenharmony_ci 6387c2aad20Sopenharmony_ci switch (OPTS_GET(hook, attach_point, 0)) { 6397c2aad20Sopenharmony_ci case BPF_TC_INGRESS: 6407c2aad20Sopenharmony_ci case BPF_TC_EGRESS: 6417c2aad20Sopenharmony_ci return libbpf_err(__bpf_tc_detach(hook, NULL, true)); 6427c2aad20Sopenharmony_ci case BPF_TC_INGRESS | BPF_TC_EGRESS: 6437c2aad20Sopenharmony_ci return libbpf_err(tc_qdisc_delete(hook)); 6447c2aad20Sopenharmony_ci case BPF_TC_CUSTOM: 6457c2aad20Sopenharmony_ci return libbpf_err(-EOPNOTSUPP); 6467c2aad20Sopenharmony_ci default: 6477c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 6487c2aad20Sopenharmony_ci } 6497c2aad20Sopenharmony_ci} 6507c2aad20Sopenharmony_ci 6517c2aad20Sopenharmony_cistruct bpf_cb_ctx { 6527c2aad20Sopenharmony_ci struct bpf_tc_opts *opts; 6537c2aad20Sopenharmony_ci bool processed; 6547c2aad20Sopenharmony_ci}; 6557c2aad20Sopenharmony_ci 6567c2aad20Sopenharmony_cistatic int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb, 6577c2aad20Sopenharmony_ci bool unicast) 6587c2aad20Sopenharmony_ci{ 6597c2aad20Sopenharmony_ci struct nlattr *tbb[TCA_BPF_MAX + 1]; 6607c2aad20Sopenharmony_ci struct bpf_cb_ctx *info = cookie; 6617c2aad20Sopenharmony_ci 6627c2aad20Sopenharmony_ci if (!info || !info->opts) 6637c2aad20Sopenharmony_ci return -EINVAL; 6647c2aad20Sopenharmony_ci if (unicast && info->processed) 6657c2aad20Sopenharmony_ci return -EINVAL; 6667c2aad20Sopenharmony_ci if (!tb[TCA_OPTIONS]) 6677c2aad20Sopenharmony_ci return NL_CONT; 6687c2aad20Sopenharmony_ci 6697c2aad20Sopenharmony_ci libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL); 6707c2aad20Sopenharmony_ci if (!tbb[TCA_BPF_ID]) 6717c2aad20Sopenharmony_ci return -EINVAL; 6727c2aad20Sopenharmony_ci 6737c2aad20Sopenharmony_ci OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID])); 6747c2aad20Sopenharmony_ci OPTS_SET(info->opts, handle, tc->tcm_handle); 6757c2aad20Sopenharmony_ci OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16); 6767c2aad20Sopenharmony_ci 6777c2aad20Sopenharmony_ci info->processed = true; 6787c2aad20Sopenharmony_ci return unicast ? NL_NEXT : NL_DONE; 6797c2aad20Sopenharmony_ci} 6807c2aad20Sopenharmony_ci 6817c2aad20Sopenharmony_cistatic int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 6827c2aad20Sopenharmony_ci void *cookie) 6837c2aad20Sopenharmony_ci{ 6847c2aad20Sopenharmony_ci struct tcmsg *tc = NLMSG_DATA(nh); 6857c2aad20Sopenharmony_ci struct nlattr *tb[TCA_MAX + 1]; 6867c2aad20Sopenharmony_ci 6877c2aad20Sopenharmony_ci libbpf_nla_parse(tb, TCA_MAX, 6887c2aad20Sopenharmony_ci (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))), 6897c2aad20Sopenharmony_ci NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL); 6907c2aad20Sopenharmony_ci if (!tb[TCA_KIND]) 6917c2aad20Sopenharmony_ci return NL_CONT; 6927c2aad20Sopenharmony_ci return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO); 6937c2aad20Sopenharmony_ci} 6947c2aad20Sopenharmony_ci 6957c2aad20Sopenharmony_cistatic int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd) 6967c2aad20Sopenharmony_ci{ 6977c2aad20Sopenharmony_ci struct bpf_prog_info info; 6987c2aad20Sopenharmony_ci __u32 info_len = sizeof(info); 6997c2aad20Sopenharmony_ci char name[256]; 7007c2aad20Sopenharmony_ci int len, ret; 7017c2aad20Sopenharmony_ci 7027c2aad20Sopenharmony_ci memset(&info, 0, info_len); 7037c2aad20Sopenharmony_ci ret = bpf_prog_get_info_by_fd(fd, &info, &info_len); 7047c2aad20Sopenharmony_ci if (ret < 0) 7057c2aad20Sopenharmony_ci return ret; 7067c2aad20Sopenharmony_ci 7077c2aad20Sopenharmony_ci ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd)); 7087c2aad20Sopenharmony_ci if (ret < 0) 7097c2aad20Sopenharmony_ci return ret; 7107c2aad20Sopenharmony_ci len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id); 7117c2aad20Sopenharmony_ci if (len < 0) 7127c2aad20Sopenharmony_ci return -errno; 7137c2aad20Sopenharmony_ci if (len >= sizeof(name)) 7147c2aad20Sopenharmony_ci return -ENAMETOOLONG; 7157c2aad20Sopenharmony_ci return nlattr_add(req, TCA_BPF_NAME, name, len + 1); 7167c2aad20Sopenharmony_ci} 7177c2aad20Sopenharmony_ci 7187c2aad20Sopenharmony_ciint bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 7197c2aad20Sopenharmony_ci{ 7207c2aad20Sopenharmony_ci __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags; 7217c2aad20Sopenharmony_ci int ret, ifindex, attach_point, prog_fd; 7227c2aad20Sopenharmony_ci struct bpf_cb_ctx info = {}; 7237c2aad20Sopenharmony_ci struct libbpf_nla_req req; 7247c2aad20Sopenharmony_ci struct nlattr *nla; 7257c2aad20Sopenharmony_ci 7267c2aad20Sopenharmony_ci if (!hook || !opts || 7277c2aad20Sopenharmony_ci !OPTS_VALID(hook, bpf_tc_hook) || 7287c2aad20Sopenharmony_ci !OPTS_VALID(opts, bpf_tc_opts)) 7297c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 7307c2aad20Sopenharmony_ci 7317c2aad20Sopenharmony_ci ifindex = OPTS_GET(hook, ifindex, 0); 7327c2aad20Sopenharmony_ci parent = OPTS_GET(hook, parent, 0); 7337c2aad20Sopenharmony_ci attach_point = OPTS_GET(hook, attach_point, 0); 7347c2aad20Sopenharmony_ci 7357c2aad20Sopenharmony_ci handle = OPTS_GET(opts, handle, 0); 7367c2aad20Sopenharmony_ci priority = OPTS_GET(opts, priority, 0); 7377c2aad20Sopenharmony_ci prog_fd = OPTS_GET(opts, prog_fd, 0); 7387c2aad20Sopenharmony_ci prog_id = OPTS_GET(opts, prog_id, 0); 7397c2aad20Sopenharmony_ci flags = OPTS_GET(opts, flags, 0); 7407c2aad20Sopenharmony_ci 7417c2aad20Sopenharmony_ci if (ifindex <= 0 || !prog_fd || prog_id) 7427c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 7437c2aad20Sopenharmony_ci if (priority > UINT16_MAX) 7447c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 7457c2aad20Sopenharmony_ci if (flags & ~BPF_TC_F_REPLACE) 7467c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 7477c2aad20Sopenharmony_ci 7487c2aad20Sopenharmony_ci flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL; 7497c2aad20Sopenharmony_ci protocol = ETH_P_ALL; 7507c2aad20Sopenharmony_ci 7517c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 7527c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 7537c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | 7547c2aad20Sopenharmony_ci NLM_F_ECHO | flags; 7557c2aad20Sopenharmony_ci req.nh.nlmsg_type = RTM_NEWTFILTER; 7567c2aad20Sopenharmony_ci req.tc.tcm_family = AF_UNSPEC; 7577c2aad20Sopenharmony_ci req.tc.tcm_ifindex = ifindex; 7587c2aad20Sopenharmony_ci req.tc.tcm_handle = handle; 7597c2aad20Sopenharmony_ci req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 7607c2aad20Sopenharmony_ci 7617c2aad20Sopenharmony_ci ret = tc_get_tcm_parent(attach_point, &parent); 7627c2aad20Sopenharmony_ci if (ret < 0) 7637c2aad20Sopenharmony_ci return libbpf_err(ret); 7647c2aad20Sopenharmony_ci req.tc.tcm_parent = parent; 7657c2aad20Sopenharmony_ci 7667c2aad20Sopenharmony_ci ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 7677c2aad20Sopenharmony_ci if (ret < 0) 7687c2aad20Sopenharmony_ci return libbpf_err(ret); 7697c2aad20Sopenharmony_ci nla = nlattr_begin_nested(&req, TCA_OPTIONS); 7707c2aad20Sopenharmony_ci if (!nla) 7717c2aad20Sopenharmony_ci return libbpf_err(-EMSGSIZE); 7727c2aad20Sopenharmony_ci ret = tc_add_fd_and_name(&req, prog_fd); 7737c2aad20Sopenharmony_ci if (ret < 0) 7747c2aad20Sopenharmony_ci return libbpf_err(ret); 7757c2aad20Sopenharmony_ci bpf_flags = TCA_BPF_FLAG_ACT_DIRECT; 7767c2aad20Sopenharmony_ci ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags)); 7777c2aad20Sopenharmony_ci if (ret < 0) 7787c2aad20Sopenharmony_ci return libbpf_err(ret); 7797c2aad20Sopenharmony_ci nlattr_end_nested(&req, nla); 7807c2aad20Sopenharmony_ci 7817c2aad20Sopenharmony_ci info.opts = opts; 7827c2aad20Sopenharmony_ci 7837c2aad20Sopenharmony_ci ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL, 7847c2aad20Sopenharmony_ci &info); 7857c2aad20Sopenharmony_ci if (ret < 0) 7867c2aad20Sopenharmony_ci return libbpf_err(ret); 7877c2aad20Sopenharmony_ci if (!info.processed) 7887c2aad20Sopenharmony_ci return libbpf_err(-ENOENT); 7897c2aad20Sopenharmony_ci return ret; 7907c2aad20Sopenharmony_ci} 7917c2aad20Sopenharmony_ci 7927c2aad20Sopenharmony_cistatic int __bpf_tc_detach(const struct bpf_tc_hook *hook, 7937c2aad20Sopenharmony_ci const struct bpf_tc_opts *opts, 7947c2aad20Sopenharmony_ci const bool flush) 7957c2aad20Sopenharmony_ci{ 7967c2aad20Sopenharmony_ci __u32 protocol = 0, handle, priority, parent, prog_id, flags; 7977c2aad20Sopenharmony_ci int ret, ifindex, attach_point, prog_fd; 7987c2aad20Sopenharmony_ci struct libbpf_nla_req req; 7997c2aad20Sopenharmony_ci 8007c2aad20Sopenharmony_ci if (!hook || 8017c2aad20Sopenharmony_ci !OPTS_VALID(hook, bpf_tc_hook) || 8027c2aad20Sopenharmony_ci !OPTS_VALID(opts, bpf_tc_opts)) 8037c2aad20Sopenharmony_ci return -EINVAL; 8047c2aad20Sopenharmony_ci 8057c2aad20Sopenharmony_ci ifindex = OPTS_GET(hook, ifindex, 0); 8067c2aad20Sopenharmony_ci parent = OPTS_GET(hook, parent, 0); 8077c2aad20Sopenharmony_ci attach_point = OPTS_GET(hook, attach_point, 0); 8087c2aad20Sopenharmony_ci 8097c2aad20Sopenharmony_ci handle = OPTS_GET(opts, handle, 0); 8107c2aad20Sopenharmony_ci priority = OPTS_GET(opts, priority, 0); 8117c2aad20Sopenharmony_ci prog_fd = OPTS_GET(opts, prog_fd, 0); 8127c2aad20Sopenharmony_ci prog_id = OPTS_GET(opts, prog_id, 0); 8137c2aad20Sopenharmony_ci flags = OPTS_GET(opts, flags, 0); 8147c2aad20Sopenharmony_ci 8157c2aad20Sopenharmony_ci if (ifindex <= 0 || flags || prog_fd || prog_id) 8167c2aad20Sopenharmony_ci return -EINVAL; 8177c2aad20Sopenharmony_ci if (priority > UINT16_MAX) 8187c2aad20Sopenharmony_ci return -EINVAL; 8197c2aad20Sopenharmony_ci if (!flush) { 8207c2aad20Sopenharmony_ci if (!handle || !priority) 8217c2aad20Sopenharmony_ci return -EINVAL; 8227c2aad20Sopenharmony_ci protocol = ETH_P_ALL; 8237c2aad20Sopenharmony_ci } else { 8247c2aad20Sopenharmony_ci if (handle || priority) 8257c2aad20Sopenharmony_ci return -EINVAL; 8267c2aad20Sopenharmony_ci } 8277c2aad20Sopenharmony_ci 8287c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 8297c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 8307c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 8317c2aad20Sopenharmony_ci req.nh.nlmsg_type = RTM_DELTFILTER; 8327c2aad20Sopenharmony_ci req.tc.tcm_family = AF_UNSPEC; 8337c2aad20Sopenharmony_ci req.tc.tcm_ifindex = ifindex; 8347c2aad20Sopenharmony_ci if (!flush) { 8357c2aad20Sopenharmony_ci req.tc.tcm_handle = handle; 8367c2aad20Sopenharmony_ci req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 8377c2aad20Sopenharmony_ci } 8387c2aad20Sopenharmony_ci 8397c2aad20Sopenharmony_ci ret = tc_get_tcm_parent(attach_point, &parent); 8407c2aad20Sopenharmony_ci if (ret < 0) 8417c2aad20Sopenharmony_ci return ret; 8427c2aad20Sopenharmony_ci req.tc.tcm_parent = parent; 8437c2aad20Sopenharmony_ci 8447c2aad20Sopenharmony_ci if (!flush) { 8457c2aad20Sopenharmony_ci ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 8467c2aad20Sopenharmony_ci if (ret < 0) 8477c2aad20Sopenharmony_ci return ret; 8487c2aad20Sopenharmony_ci } 8497c2aad20Sopenharmony_ci 8507c2aad20Sopenharmony_ci return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL); 8517c2aad20Sopenharmony_ci} 8527c2aad20Sopenharmony_ci 8537c2aad20Sopenharmony_ciint bpf_tc_detach(const struct bpf_tc_hook *hook, 8547c2aad20Sopenharmony_ci const struct bpf_tc_opts *opts) 8557c2aad20Sopenharmony_ci{ 8567c2aad20Sopenharmony_ci int ret; 8577c2aad20Sopenharmony_ci 8587c2aad20Sopenharmony_ci if (!opts) 8597c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 8607c2aad20Sopenharmony_ci 8617c2aad20Sopenharmony_ci ret = __bpf_tc_detach(hook, opts, false); 8627c2aad20Sopenharmony_ci return libbpf_err(ret); 8637c2aad20Sopenharmony_ci} 8647c2aad20Sopenharmony_ci 8657c2aad20Sopenharmony_ciint bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 8667c2aad20Sopenharmony_ci{ 8677c2aad20Sopenharmony_ci __u32 protocol, handle, priority, parent, prog_id, flags; 8687c2aad20Sopenharmony_ci int ret, ifindex, attach_point, prog_fd; 8697c2aad20Sopenharmony_ci struct bpf_cb_ctx info = {}; 8707c2aad20Sopenharmony_ci struct libbpf_nla_req req; 8717c2aad20Sopenharmony_ci 8727c2aad20Sopenharmony_ci if (!hook || !opts || 8737c2aad20Sopenharmony_ci !OPTS_VALID(hook, bpf_tc_hook) || 8747c2aad20Sopenharmony_ci !OPTS_VALID(opts, bpf_tc_opts)) 8757c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 8767c2aad20Sopenharmony_ci 8777c2aad20Sopenharmony_ci ifindex = OPTS_GET(hook, ifindex, 0); 8787c2aad20Sopenharmony_ci parent = OPTS_GET(hook, parent, 0); 8797c2aad20Sopenharmony_ci attach_point = OPTS_GET(hook, attach_point, 0); 8807c2aad20Sopenharmony_ci 8817c2aad20Sopenharmony_ci handle = OPTS_GET(opts, handle, 0); 8827c2aad20Sopenharmony_ci priority = OPTS_GET(opts, priority, 0); 8837c2aad20Sopenharmony_ci prog_fd = OPTS_GET(opts, prog_fd, 0); 8847c2aad20Sopenharmony_ci prog_id = OPTS_GET(opts, prog_id, 0); 8857c2aad20Sopenharmony_ci flags = OPTS_GET(opts, flags, 0); 8867c2aad20Sopenharmony_ci 8877c2aad20Sopenharmony_ci if (ifindex <= 0 || flags || prog_fd || prog_id || 8887c2aad20Sopenharmony_ci !handle || !priority) 8897c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 8907c2aad20Sopenharmony_ci if (priority > UINT16_MAX) 8917c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 8927c2aad20Sopenharmony_ci 8937c2aad20Sopenharmony_ci protocol = ETH_P_ALL; 8947c2aad20Sopenharmony_ci 8957c2aad20Sopenharmony_ci memset(&req, 0, sizeof(req)); 8967c2aad20Sopenharmony_ci req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 8977c2aad20Sopenharmony_ci req.nh.nlmsg_flags = NLM_F_REQUEST; 8987c2aad20Sopenharmony_ci req.nh.nlmsg_type = RTM_GETTFILTER; 8997c2aad20Sopenharmony_ci req.tc.tcm_family = AF_UNSPEC; 9007c2aad20Sopenharmony_ci req.tc.tcm_ifindex = ifindex; 9017c2aad20Sopenharmony_ci req.tc.tcm_handle = handle; 9027c2aad20Sopenharmony_ci req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 9037c2aad20Sopenharmony_ci 9047c2aad20Sopenharmony_ci ret = tc_get_tcm_parent(attach_point, &parent); 9057c2aad20Sopenharmony_ci if (ret < 0) 9067c2aad20Sopenharmony_ci return libbpf_err(ret); 9077c2aad20Sopenharmony_ci req.tc.tcm_parent = parent; 9087c2aad20Sopenharmony_ci 9097c2aad20Sopenharmony_ci ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 9107c2aad20Sopenharmony_ci if (ret < 0) 9117c2aad20Sopenharmony_ci return libbpf_err(ret); 9127c2aad20Sopenharmony_ci 9137c2aad20Sopenharmony_ci info.opts = opts; 9147c2aad20Sopenharmony_ci 9157c2aad20Sopenharmony_ci ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL, 9167c2aad20Sopenharmony_ci &info); 9177c2aad20Sopenharmony_ci if (ret < 0) 9187c2aad20Sopenharmony_ci return libbpf_err(ret); 9197c2aad20Sopenharmony_ci if (!info.processed) 9207c2aad20Sopenharmony_ci return libbpf_err(-ENOENT); 9217c2aad20Sopenharmony_ci return ret; 9227c2aad20Sopenharmony_ci} 923