11bc38b8fSAlexei Starovoitov // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2f7010770SYonghong Song /* Copyright (c) 2018 Facebook */ 3f7010770SYonghong Song 4f7010770SYonghong Song #include <stdlib.h> 5f7010770SYonghong Song #include <memory.h> 6f7010770SYonghong Song #include <unistd.h> 7715c5ce4SKumar Kartikeya Dwivedi #include <arpa/inet.h> 8f7010770SYonghong Song #include <linux/bpf.h> 9715c5ce4SKumar Kartikeya Dwivedi #include <linux/if_ether.h> 10715c5ce4SKumar Kartikeya Dwivedi #include <linux/pkt_cls.h> 11f7010770SYonghong Song #include <linux/rtnetlink.h> 12f7010770SYonghong Song #include <sys/socket.h> 13f7010770SYonghong Song #include <errno.h> 14f7010770SYonghong Song #include <time.h> 15f7010770SYonghong Song 16f7010770SYonghong Song #include "bpf.h" 17f7010770SYonghong Song #include "libbpf.h" 18b6e99b01SToke Høiland-Jørgensen #include "libbpf_internal.h" 19f7010770SYonghong Song #include "nlattr.h" 20f7010770SYonghong Song 21f7010770SYonghong Song #ifndef SOL_NETLINK 22f7010770SYonghong Song #define SOL_NETLINK 270 23f7010770SYonghong Song #endif 24f7010770SYonghong Song 257084566aSAndrii Nakryiko typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 267084566aSAndrii Nakryiko 27aae57780SAndrey Ignatov typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 28434fe9d4SAndrey Ignatov void *cookie); 29434fe9d4SAndrey Ignatov 3050db9f07SMaciej Fijalkowski struct xdp_id_md { 3150db9f07SMaciej Fijalkowski int ifindex; 3250db9f07SMaciej Fijalkowski __u32 flags; 33473f4e13SToke Høiland-Jørgensen struct xdp_link_info info; 3450db9f07SMaciej Fijalkowski }; 3550db9f07SMaciej Fijalkowski 367084566aSAndrii Nakryiko static int libbpf_netlink_open(__u32 *nl_pid) 37f7010770SYonghong Song { 38f7010770SYonghong Song struct sockaddr_nl sa; 39f7010770SYonghong Song socklen_t addrlen; 40f7010770SYonghong Song int one = 1, ret; 41f7010770SYonghong Song int sock; 42f7010770SYonghong Song 43f7010770SYonghong Song memset(&sa, 0, sizeof(sa)); 44f7010770SYonghong Song sa.nl_family = AF_NETLINK; 45f7010770SYonghong Song 4658bfd95bSKumar Kartikeya Dwivedi sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); 47f7010770SYonghong Song if (sock < 0) 48f7010770SYonghong Song return -errno; 49f7010770SYonghong Song 50f7010770SYonghong Song if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, 51f7010770SYonghong Song &one, sizeof(one)) < 0) { 52b6e99b01SToke Høiland-Jørgensen pr_warn("Netlink error reporting not supported\n"); 53f7010770SYonghong Song } 54f7010770SYonghong Song 55f7010770SYonghong Song if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 56f7010770SYonghong Song ret = -errno; 57f7010770SYonghong Song goto cleanup; 58f7010770SYonghong Song } 59f7010770SYonghong Song 60f7010770SYonghong Song addrlen = sizeof(sa); 61f7010770SYonghong Song if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { 62f7010770SYonghong Song ret = -errno; 63f7010770SYonghong Song goto cleanup; 64f7010770SYonghong Song } 65f7010770SYonghong Song 66f7010770SYonghong Song if (addrlen != sizeof(sa)) { 67f7010770SYonghong Song ret = -LIBBPF_ERRNO__INTERNAL; 68f7010770SYonghong Song goto cleanup; 69f7010770SYonghong Song } 70f7010770SYonghong Song 71f7010770SYonghong Song *nl_pid = sa.nl_pid; 72f7010770SYonghong Song return sock; 73f7010770SYonghong Song 74f7010770SYonghong Song cleanup: 75f7010770SYonghong Song close(sock); 76f7010770SYonghong Song return ret; 77f7010770SYonghong Song } 78f7010770SYonghong Song 798bbb77b7SKumar Kartikeya Dwivedi static void libbpf_netlink_close(int sock) 808bbb77b7SKumar Kartikeya Dwivedi { 818bbb77b7SKumar Kartikeya Dwivedi close(sock); 828bbb77b7SKumar Kartikeya Dwivedi } 838bbb77b7SKumar Kartikeya Dwivedi 84715c5ce4SKumar Kartikeya Dwivedi enum { 85715c5ce4SKumar Kartikeya Dwivedi NL_CONT, 86715c5ce4SKumar Kartikeya Dwivedi NL_NEXT, 87715c5ce4SKumar Kartikeya Dwivedi NL_DONE, 88715c5ce4SKumar Kartikeya Dwivedi }; 89715c5ce4SKumar Kartikeya Dwivedi 908bbb77b7SKumar Kartikeya Dwivedi static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq, 91aae57780SAndrey Ignatov __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn, 9236f1678dSYonghong Song void *cookie) 93f7010770SYonghong Song { 949d0b3c1fSYonghong Song bool multipart = true; 95f7010770SYonghong Song struct nlmsgerr *err; 96f7010770SYonghong Song struct nlmsghdr *nh; 97f7010770SYonghong Song char buf[4096]; 98f7010770SYonghong Song int len, ret; 99f7010770SYonghong Song 1009d0b3c1fSYonghong Song while (multipart) { 101715c5ce4SKumar Kartikeya Dwivedi start: 1029d0b3c1fSYonghong Song multipart = false; 103f7010770SYonghong Song len = recv(sock, buf, sizeof(buf), 0); 104f7010770SYonghong Song if (len < 0) { 105f7010770SYonghong Song ret = -errno; 106f7010770SYonghong Song goto done; 107f7010770SYonghong Song } 108f7010770SYonghong Song 1099d0b3c1fSYonghong Song if (len == 0) 1109d0b3c1fSYonghong Song break; 1119d0b3c1fSYonghong Song 112f7010770SYonghong Song for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); 113f7010770SYonghong Song nh = NLMSG_NEXT(nh, len)) { 114f7010770SYonghong Song if (nh->nlmsg_pid != nl_pid) { 115f7010770SYonghong Song ret = -LIBBPF_ERRNO__WRNGPID; 116f7010770SYonghong Song goto done; 117f7010770SYonghong Song } 118f7010770SYonghong Song if (nh->nlmsg_seq != seq) { 119f7010770SYonghong Song ret = -LIBBPF_ERRNO__INVSEQ; 120f7010770SYonghong Song goto done; 121f7010770SYonghong Song } 1229d0b3c1fSYonghong Song if (nh->nlmsg_flags & NLM_F_MULTI) 1239d0b3c1fSYonghong Song multipart = true; 124f7010770SYonghong Song switch (nh->nlmsg_type) { 125f7010770SYonghong Song case NLMSG_ERROR: 126f7010770SYonghong Song err = (struct nlmsgerr *)NLMSG_DATA(nh); 127f7010770SYonghong Song if (!err->error) 128f7010770SYonghong Song continue; 129f7010770SYonghong Song ret = err->error; 130f04bc8a4SAndrey Ignatov libbpf_nla_dump_errormsg(nh); 131f7010770SYonghong Song goto done; 132f7010770SYonghong Song case NLMSG_DONE: 133f7010770SYonghong Song return 0; 134f7010770SYonghong Song default: 135f7010770SYonghong Song break; 136f7010770SYonghong Song } 13736f1678dSYonghong Song if (_fn) { 13836f1678dSYonghong Song ret = _fn(nh, fn, cookie); 139715c5ce4SKumar Kartikeya Dwivedi switch (ret) { 140715c5ce4SKumar Kartikeya Dwivedi case NL_CONT: 141715c5ce4SKumar Kartikeya Dwivedi break; 142715c5ce4SKumar Kartikeya Dwivedi case NL_NEXT: 143715c5ce4SKumar Kartikeya Dwivedi goto start; 144715c5ce4SKumar Kartikeya Dwivedi case NL_DONE: 145715c5ce4SKumar Kartikeya Dwivedi return 0; 146715c5ce4SKumar Kartikeya Dwivedi default: 14736f1678dSYonghong Song return ret; 14836f1678dSYonghong Song } 149f7010770SYonghong Song } 150f7010770SYonghong Song } 151715c5ce4SKumar Kartikeya Dwivedi } 152f7010770SYonghong Song ret = 0; 153f7010770SYonghong Song done: 154f7010770SYonghong Song return ret; 155f7010770SYonghong Song } 156f7010770SYonghong Song 1570ae64fb6SKumar Kartikeya Dwivedi static int libbpf_netlink_send_recv(struct libbpf_nla_req *req, 1588bbb77b7SKumar Kartikeya Dwivedi __dump_nlmsg_t parse_msg, 1598bbb77b7SKumar Kartikeya Dwivedi libbpf_dump_nlmsg_t parse_attr, 1608bbb77b7SKumar Kartikeya Dwivedi void *cookie) 161f7010770SYonghong Song { 1624734b0feSJeremy Cline __u32 nl_pid = 0; 1638bbb77b7SKumar Kartikeya Dwivedi int sock, ret; 164f7010770SYonghong Song 165aae57780SAndrey Ignatov sock = libbpf_netlink_open(&nl_pid); 166f7010770SYonghong Song if (sock < 0) 167f7010770SYonghong Song return sock; 168f7010770SYonghong Song 1690ae64fb6SKumar Kartikeya Dwivedi req->nh.nlmsg_pid = 0; 1700ae64fb6SKumar Kartikeya Dwivedi req->nh.nlmsg_seq = time(NULL); 1718bbb77b7SKumar Kartikeya Dwivedi 1720ae64fb6SKumar Kartikeya Dwivedi if (send(sock, req, req->nh.nlmsg_len, 0) < 0) { 1738bbb77b7SKumar Kartikeya Dwivedi ret = -errno; 1748bbb77b7SKumar Kartikeya Dwivedi goto out; 1758bbb77b7SKumar Kartikeya Dwivedi } 1768bbb77b7SKumar Kartikeya Dwivedi 1770ae64fb6SKumar Kartikeya Dwivedi ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq, 1788bbb77b7SKumar Kartikeya Dwivedi parse_msg, parse_attr, cookie); 1798bbb77b7SKumar Kartikeya Dwivedi out: 1808bbb77b7SKumar Kartikeya Dwivedi libbpf_netlink_close(sock); 1818bbb77b7SKumar Kartikeya Dwivedi return ret; 1828bbb77b7SKumar Kartikeya Dwivedi } 1838bbb77b7SKumar Kartikeya Dwivedi 1848bbb77b7SKumar Kartikeya Dwivedi static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd, 1858bbb77b7SKumar Kartikeya Dwivedi __u32 flags) 1868bbb77b7SKumar Kartikeya Dwivedi { 1878bbb77b7SKumar Kartikeya Dwivedi struct nlattr *nla; 1888bbb77b7SKumar Kartikeya Dwivedi int ret; 1890ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req; 1908bbb77b7SKumar Kartikeya Dwivedi 191f7010770SYonghong Song memset(&req, 0, sizeof(req)); 192f7010770SYonghong Song req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 193f7010770SYonghong Song req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 194f7010770SYonghong Song req.nh.nlmsg_type = RTM_SETLINK; 195f7010770SYonghong Song req.ifinfo.ifi_family = AF_UNSPEC; 196f7010770SYonghong Song req.ifinfo.ifi_index = ifindex; 197f7010770SYonghong Song 1980ae64fb6SKumar Kartikeya Dwivedi nla = nlattr_begin_nested(&req, IFLA_XDP); 1998bbb77b7SKumar Kartikeya Dwivedi if (!nla) 2008bbb77b7SKumar Kartikeya Dwivedi return -EMSGSIZE; 2010ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd)); 2028bbb77b7SKumar Kartikeya Dwivedi if (ret < 0) 203f7010770SYonghong Song return ret; 2048bbb77b7SKumar Kartikeya Dwivedi if (flags) { 2050ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags)); 2068bbb77b7SKumar Kartikeya Dwivedi if (ret < 0) 2078bbb77b7SKumar Kartikeya Dwivedi return ret; 2088bbb77b7SKumar Kartikeya Dwivedi } 2098bbb77b7SKumar Kartikeya Dwivedi if (flags & XDP_FLAGS_REPLACE) { 2100ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd, 2110ae64fb6SKumar Kartikeya Dwivedi sizeof(old_fd)); 2128bbb77b7SKumar Kartikeya Dwivedi if (ret < 0) 2138bbb77b7SKumar Kartikeya Dwivedi return ret; 2148bbb77b7SKumar Kartikeya Dwivedi } 2150ae64fb6SKumar Kartikeya Dwivedi nlattr_end_nested(&req, nla); 2168bbb77b7SKumar Kartikeya Dwivedi 2170ae64fb6SKumar Kartikeya Dwivedi return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); 218f7010770SYonghong Song } 21936f1678dSYonghong Song 220bd5ca3efSToke Høiland-Jørgensen int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, 221bd5ca3efSToke Høiland-Jørgensen const struct bpf_xdp_set_link_opts *opts) 222bd5ca3efSToke Høiland-Jørgensen { 223e9fc3ce9SAndrii Nakryiko int old_fd = -1, ret; 224bd5ca3efSToke Høiland-Jørgensen 225bd5ca3efSToke Høiland-Jørgensen if (!OPTS_VALID(opts, bpf_xdp_set_link_opts)) 226e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 227bd5ca3efSToke Høiland-Jørgensen 228bd5ca3efSToke Høiland-Jørgensen if (OPTS_HAS(opts, old_fd)) { 229bd5ca3efSToke Høiland-Jørgensen old_fd = OPTS_GET(opts, old_fd, -1); 230bd5ca3efSToke Høiland-Jørgensen flags |= XDP_FLAGS_REPLACE; 231bd5ca3efSToke Høiland-Jørgensen } 232bd5ca3efSToke Høiland-Jørgensen 233e9fc3ce9SAndrii Nakryiko ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags); 234e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 235bd5ca3efSToke Høiland-Jørgensen } 236bd5ca3efSToke Høiland-Jørgensen 237bd5ca3efSToke Høiland-Jørgensen int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 238bd5ca3efSToke Høiland-Jørgensen { 239e9fc3ce9SAndrii Nakryiko int ret; 240e9fc3ce9SAndrii Nakryiko 241e9fc3ce9SAndrii Nakryiko ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags); 242e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 243bd5ca3efSToke Høiland-Jørgensen } 244bd5ca3efSToke Høiland-Jørgensen 245aae57780SAndrey Ignatov static int __dump_link_nlmsg(struct nlmsghdr *nlh, 246aae57780SAndrey Ignatov libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 24736f1678dSYonghong Song { 24836f1678dSYonghong Song struct nlattr *tb[IFLA_MAX + 1], *attr; 24936f1678dSYonghong Song struct ifinfomsg *ifi = NLMSG_DATA(nlh); 25036f1678dSYonghong Song int len; 25136f1678dSYonghong Song 25236f1678dSYonghong Song len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 25336f1678dSYonghong Song attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 2548bbb77b7SKumar Kartikeya Dwivedi 255f04bc8a4SAndrey Ignatov if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 25636f1678dSYonghong Song return -LIBBPF_ERRNO__NLPARSE; 25736f1678dSYonghong Song 25836f1678dSYonghong Song return dump_link_nlmsg(cookie, ifi, tb); 25936f1678dSYonghong Song } 26036f1678dSYonghong Song 261473f4e13SToke Høiland-Jørgensen static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) 26250db9f07SMaciej Fijalkowski { 26350db9f07SMaciej Fijalkowski struct nlattr *xdp_tb[IFLA_XDP_MAX + 1]; 26450db9f07SMaciej Fijalkowski struct xdp_id_md *xdp_id = cookie; 26550db9f07SMaciej Fijalkowski struct ifinfomsg *ifinfo = msg; 26650db9f07SMaciej Fijalkowski int ret; 26750db9f07SMaciej Fijalkowski 26850db9f07SMaciej Fijalkowski if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index) 26950db9f07SMaciej Fijalkowski return 0; 27050db9f07SMaciej Fijalkowski 27150db9f07SMaciej Fijalkowski if (!tb[IFLA_XDP]) 27250db9f07SMaciej Fijalkowski return 0; 27350db9f07SMaciej Fijalkowski 27450db9f07SMaciej Fijalkowski ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL); 27550db9f07SMaciej Fijalkowski if (ret) 27650db9f07SMaciej Fijalkowski return ret; 27750db9f07SMaciej Fijalkowski 27850db9f07SMaciej Fijalkowski if (!xdp_tb[IFLA_XDP_ATTACHED]) 27950db9f07SMaciej Fijalkowski return 0; 28050db9f07SMaciej Fijalkowski 281473f4e13SToke Høiland-Jørgensen xdp_id->info.attach_mode = libbpf_nla_getattr_u8( 282473f4e13SToke Høiland-Jørgensen xdp_tb[IFLA_XDP_ATTACHED]); 283473f4e13SToke Høiland-Jørgensen 284473f4e13SToke Høiland-Jørgensen if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE) 28550db9f07SMaciej Fijalkowski return 0; 28650db9f07SMaciej Fijalkowski 287473f4e13SToke Høiland-Jørgensen if (xdp_tb[IFLA_XDP_PROG_ID]) 288473f4e13SToke Høiland-Jørgensen xdp_id->info.prog_id = libbpf_nla_getattr_u32( 289473f4e13SToke Høiland-Jørgensen xdp_tb[IFLA_XDP_PROG_ID]); 29050db9f07SMaciej Fijalkowski 291473f4e13SToke Høiland-Jørgensen if (xdp_tb[IFLA_XDP_SKB_PROG_ID]) 292473f4e13SToke Høiland-Jørgensen xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32( 293473f4e13SToke Høiland-Jørgensen xdp_tb[IFLA_XDP_SKB_PROG_ID]); 294473f4e13SToke Høiland-Jørgensen 295473f4e13SToke Høiland-Jørgensen if (xdp_tb[IFLA_XDP_DRV_PROG_ID]) 296473f4e13SToke Høiland-Jørgensen xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32( 297473f4e13SToke Høiland-Jørgensen xdp_tb[IFLA_XDP_DRV_PROG_ID]); 298473f4e13SToke Høiland-Jørgensen 299473f4e13SToke Høiland-Jørgensen if (xdp_tb[IFLA_XDP_HW_PROG_ID]) 300473f4e13SToke Høiland-Jørgensen xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32( 301473f4e13SToke Høiland-Jørgensen xdp_tb[IFLA_XDP_HW_PROG_ID]); 30250db9f07SMaciej Fijalkowski 30350db9f07SMaciej Fijalkowski return 0; 30450db9f07SMaciej Fijalkowski } 30550db9f07SMaciej Fijalkowski 306473f4e13SToke Høiland-Jørgensen int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, 307473f4e13SToke Høiland-Jørgensen size_t info_size, __u32 flags) 30850db9f07SMaciej Fijalkowski { 30950db9f07SMaciej Fijalkowski struct xdp_id_md xdp_id = {}; 31050db9f07SMaciej Fijalkowski __u32 mask; 3118bbb77b7SKumar Kartikeya Dwivedi int ret; 3120ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req = { 3138bbb77b7SKumar Kartikeya Dwivedi .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 3148bbb77b7SKumar Kartikeya Dwivedi .nh.nlmsg_type = RTM_GETLINK, 3158bbb77b7SKumar Kartikeya Dwivedi .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 3160ae64fb6SKumar Kartikeya Dwivedi .ifinfo.ifi_family = AF_PACKET, 3178bbb77b7SKumar Kartikeya Dwivedi }; 31850db9f07SMaciej Fijalkowski 319473f4e13SToke Høiland-Jørgensen if (flags & ~XDP_FLAGS_MASK || !info_size) 320e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 32150db9f07SMaciej Fijalkowski 32250db9f07SMaciej Fijalkowski /* Check whether the single {HW,DRV,SKB} mode is set */ 32350db9f07SMaciej Fijalkowski flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE); 32450db9f07SMaciej Fijalkowski mask = flags - 1; 32550db9f07SMaciej Fijalkowski if (flags && flags & mask) 326e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 32750db9f07SMaciej Fijalkowski 32850db9f07SMaciej Fijalkowski xdp_id.ifindex = ifindex; 32950db9f07SMaciej Fijalkowski xdp_id.flags = flags; 33050db9f07SMaciej Fijalkowski 3310ae64fb6SKumar Kartikeya Dwivedi ret = libbpf_netlink_send_recv(&req, __dump_link_nlmsg, 3328bbb77b7SKumar Kartikeya Dwivedi get_xdp_info, &xdp_id); 333473f4e13SToke Høiland-Jørgensen if (!ret) { 334473f4e13SToke Høiland-Jørgensen size_t sz = min(info_size, sizeof(xdp_id.info)); 335473f4e13SToke Høiland-Jørgensen 336473f4e13SToke Høiland-Jørgensen memcpy(info, &xdp_id.info, sz); 337473f4e13SToke Høiland-Jørgensen memset((void *) info + sz, 0, info_size - sz); 338473f4e13SToke Høiland-Jørgensen } 33950db9f07SMaciej Fijalkowski 340e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 34150db9f07SMaciej Fijalkowski } 34250db9f07SMaciej Fijalkowski 343473f4e13SToke Høiland-Jørgensen static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags) 344473f4e13SToke Høiland-Jørgensen { 345257d7d4fSDavid Ahern flags &= XDP_FLAGS_MODES; 346257d7d4fSDavid Ahern 347f07cbad2SAndrey Ignatov if (info->attach_mode != XDP_ATTACHED_MULTI && !flags) 348473f4e13SToke Høiland-Jørgensen return info->prog_id; 349473f4e13SToke Høiland-Jørgensen if (flags & XDP_FLAGS_DRV_MODE) 350473f4e13SToke Høiland-Jørgensen return info->drv_prog_id; 351473f4e13SToke Høiland-Jørgensen if (flags & XDP_FLAGS_HW_MODE) 352473f4e13SToke Høiland-Jørgensen return info->hw_prog_id; 353473f4e13SToke Høiland-Jørgensen if (flags & XDP_FLAGS_SKB_MODE) 354473f4e13SToke Høiland-Jørgensen return info->skb_prog_id; 355473f4e13SToke Høiland-Jørgensen 356473f4e13SToke Høiland-Jørgensen return 0; 357473f4e13SToke Høiland-Jørgensen } 358473f4e13SToke Høiland-Jørgensen 359473f4e13SToke Høiland-Jørgensen int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) 360473f4e13SToke Høiland-Jørgensen { 361473f4e13SToke Høiland-Jørgensen struct xdp_link_info info; 362473f4e13SToke Høiland-Jørgensen int ret; 363473f4e13SToke Høiland-Jørgensen 364473f4e13SToke Høiland-Jørgensen ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags); 365473f4e13SToke Høiland-Jørgensen if (!ret) 366473f4e13SToke Høiland-Jørgensen *prog_id = get_xdp_id(&info, flags); 367473f4e13SToke Høiland-Jørgensen 368e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 369473f4e13SToke Høiland-Jørgensen } 370715c5ce4SKumar Kartikeya Dwivedi 3710ae64fb6SKumar Kartikeya Dwivedi typedef int (*qdisc_config_t)(struct libbpf_nla_req *req); 372715c5ce4SKumar Kartikeya Dwivedi 3730ae64fb6SKumar Kartikeya Dwivedi static int clsact_config(struct libbpf_nla_req *req) 374715c5ce4SKumar Kartikeya Dwivedi { 3750ae64fb6SKumar Kartikeya Dwivedi req->tc.tcm_parent = TC_H_CLSACT; 3760ae64fb6SKumar Kartikeya Dwivedi req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); 377715c5ce4SKumar Kartikeya Dwivedi 3780ae64fb6SKumar Kartikeya Dwivedi return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact")); 379715c5ce4SKumar Kartikeya Dwivedi } 380715c5ce4SKumar Kartikeya Dwivedi 381715c5ce4SKumar Kartikeya Dwivedi static int attach_point_to_config(struct bpf_tc_hook *hook, 382715c5ce4SKumar Kartikeya Dwivedi qdisc_config_t *config) 383715c5ce4SKumar Kartikeya Dwivedi { 384715c5ce4SKumar Kartikeya Dwivedi switch (OPTS_GET(hook, attach_point, 0)) { 385715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_INGRESS: 386715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_EGRESS: 387715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_INGRESS | BPF_TC_EGRESS: 388715c5ce4SKumar Kartikeya Dwivedi if (OPTS_GET(hook, parent, 0)) 389715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 390715c5ce4SKumar Kartikeya Dwivedi *config = &clsact_config; 391715c5ce4SKumar Kartikeya Dwivedi return 0; 392715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_CUSTOM: 393715c5ce4SKumar Kartikeya Dwivedi return -EOPNOTSUPP; 394715c5ce4SKumar Kartikeya Dwivedi default: 395715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 396715c5ce4SKumar Kartikeya Dwivedi } 397715c5ce4SKumar Kartikeya Dwivedi } 398715c5ce4SKumar Kartikeya Dwivedi 399715c5ce4SKumar Kartikeya Dwivedi static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point, 400715c5ce4SKumar Kartikeya Dwivedi __u32 *parent) 401715c5ce4SKumar Kartikeya Dwivedi { 402715c5ce4SKumar Kartikeya Dwivedi switch (attach_point) { 403715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_INGRESS: 404715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_EGRESS: 405715c5ce4SKumar Kartikeya Dwivedi if (*parent) 406715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 407715c5ce4SKumar Kartikeya Dwivedi *parent = TC_H_MAKE(TC_H_CLSACT, 408715c5ce4SKumar Kartikeya Dwivedi attach_point == BPF_TC_INGRESS ? 409715c5ce4SKumar Kartikeya Dwivedi TC_H_MIN_INGRESS : TC_H_MIN_EGRESS); 410715c5ce4SKumar Kartikeya Dwivedi break; 411715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_CUSTOM: 412715c5ce4SKumar Kartikeya Dwivedi if (!*parent) 413715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 414715c5ce4SKumar Kartikeya Dwivedi break; 415715c5ce4SKumar Kartikeya Dwivedi default: 416715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 417715c5ce4SKumar Kartikeya Dwivedi } 418715c5ce4SKumar Kartikeya Dwivedi return 0; 419715c5ce4SKumar Kartikeya Dwivedi } 420715c5ce4SKumar Kartikeya Dwivedi 421715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags) 422715c5ce4SKumar Kartikeya Dwivedi { 423715c5ce4SKumar Kartikeya Dwivedi qdisc_config_t config; 424715c5ce4SKumar Kartikeya Dwivedi int ret; 4250ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req; 426715c5ce4SKumar Kartikeya Dwivedi 427715c5ce4SKumar Kartikeya Dwivedi ret = attach_point_to_config(hook, &config); 428715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 429715c5ce4SKumar Kartikeya Dwivedi return ret; 430715c5ce4SKumar Kartikeya Dwivedi 431715c5ce4SKumar Kartikeya Dwivedi memset(&req, 0, sizeof(req)); 432715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 433715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; 434715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_type = cmd; 435715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_family = AF_UNSPEC; 436715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0); 437715c5ce4SKumar Kartikeya Dwivedi 4380ae64fb6SKumar Kartikeya Dwivedi ret = config(&req); 439715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 440715c5ce4SKumar Kartikeya Dwivedi return ret; 441715c5ce4SKumar Kartikeya Dwivedi 4420ae64fb6SKumar Kartikeya Dwivedi return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); 443715c5ce4SKumar Kartikeya Dwivedi } 444715c5ce4SKumar Kartikeya Dwivedi 445715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_create_excl(struct bpf_tc_hook *hook) 446715c5ce4SKumar Kartikeya Dwivedi { 447bbf29d3aSKumar Kartikeya Dwivedi return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL); 448715c5ce4SKumar Kartikeya Dwivedi } 449715c5ce4SKumar Kartikeya Dwivedi 450715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_delete(struct bpf_tc_hook *hook) 451715c5ce4SKumar Kartikeya Dwivedi { 452715c5ce4SKumar Kartikeya Dwivedi return tc_qdisc_modify(hook, RTM_DELQDISC, 0); 453715c5ce4SKumar Kartikeya Dwivedi } 454715c5ce4SKumar Kartikeya Dwivedi 455715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_hook_create(struct bpf_tc_hook *hook) 456715c5ce4SKumar Kartikeya Dwivedi { 457e9fc3ce9SAndrii Nakryiko int ret; 458e9fc3ce9SAndrii Nakryiko 459715c5ce4SKumar Kartikeya Dwivedi if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 460715c5ce4SKumar Kartikeya Dwivedi OPTS_GET(hook, ifindex, 0) <= 0) 461e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 462715c5ce4SKumar Kartikeya Dwivedi 463e9fc3ce9SAndrii Nakryiko ret = tc_qdisc_create_excl(hook); 464e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 465715c5ce4SKumar Kartikeya Dwivedi } 466715c5ce4SKumar Kartikeya Dwivedi 467715c5ce4SKumar Kartikeya Dwivedi static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 468715c5ce4SKumar Kartikeya Dwivedi const struct bpf_tc_opts *opts, 469715c5ce4SKumar Kartikeya Dwivedi const bool flush); 470715c5ce4SKumar Kartikeya Dwivedi 471715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_hook_destroy(struct bpf_tc_hook *hook) 472715c5ce4SKumar Kartikeya Dwivedi { 473715c5ce4SKumar Kartikeya Dwivedi if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 474715c5ce4SKumar Kartikeya Dwivedi OPTS_GET(hook, ifindex, 0) <= 0) 475e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 476715c5ce4SKumar Kartikeya Dwivedi 477715c5ce4SKumar Kartikeya Dwivedi switch (OPTS_GET(hook, attach_point, 0)) { 478715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_INGRESS: 479715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_EGRESS: 480e9fc3ce9SAndrii Nakryiko return libbpf_err(__bpf_tc_detach(hook, NULL, true)); 481715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_INGRESS | BPF_TC_EGRESS: 482e9fc3ce9SAndrii Nakryiko return libbpf_err(tc_qdisc_delete(hook)); 483715c5ce4SKumar Kartikeya Dwivedi case BPF_TC_CUSTOM: 484e9fc3ce9SAndrii Nakryiko return libbpf_err(-EOPNOTSUPP); 485715c5ce4SKumar Kartikeya Dwivedi default: 486e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 487715c5ce4SKumar Kartikeya Dwivedi } 488715c5ce4SKumar Kartikeya Dwivedi } 489715c5ce4SKumar Kartikeya Dwivedi 490715c5ce4SKumar Kartikeya Dwivedi struct bpf_cb_ctx { 491715c5ce4SKumar Kartikeya Dwivedi struct bpf_tc_opts *opts; 492715c5ce4SKumar Kartikeya Dwivedi bool processed; 493715c5ce4SKumar Kartikeya Dwivedi }; 494715c5ce4SKumar Kartikeya Dwivedi 495715c5ce4SKumar Kartikeya Dwivedi static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb, 496715c5ce4SKumar Kartikeya Dwivedi bool unicast) 497715c5ce4SKumar Kartikeya Dwivedi { 498715c5ce4SKumar Kartikeya Dwivedi struct nlattr *tbb[TCA_BPF_MAX + 1]; 499715c5ce4SKumar Kartikeya Dwivedi struct bpf_cb_ctx *info = cookie; 500715c5ce4SKumar Kartikeya Dwivedi 501715c5ce4SKumar Kartikeya Dwivedi if (!info || !info->opts) 502715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 503715c5ce4SKumar Kartikeya Dwivedi if (unicast && info->processed) 504715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 505715c5ce4SKumar Kartikeya Dwivedi if (!tb[TCA_OPTIONS]) 506715c5ce4SKumar Kartikeya Dwivedi return NL_CONT; 507715c5ce4SKumar Kartikeya Dwivedi 508715c5ce4SKumar Kartikeya Dwivedi libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL); 509715c5ce4SKumar Kartikeya Dwivedi if (!tbb[TCA_BPF_ID]) 510715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 511715c5ce4SKumar Kartikeya Dwivedi 512715c5ce4SKumar Kartikeya Dwivedi OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID])); 513715c5ce4SKumar Kartikeya Dwivedi OPTS_SET(info->opts, handle, tc->tcm_handle); 514715c5ce4SKumar Kartikeya Dwivedi OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16); 515715c5ce4SKumar Kartikeya Dwivedi 516715c5ce4SKumar Kartikeya Dwivedi info->processed = true; 517715c5ce4SKumar Kartikeya Dwivedi return unicast ? NL_NEXT : NL_DONE; 518715c5ce4SKumar Kartikeya Dwivedi } 519715c5ce4SKumar Kartikeya Dwivedi 520715c5ce4SKumar Kartikeya Dwivedi static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 521715c5ce4SKumar Kartikeya Dwivedi void *cookie) 522715c5ce4SKumar Kartikeya Dwivedi { 523715c5ce4SKumar Kartikeya Dwivedi struct tcmsg *tc = NLMSG_DATA(nh); 524715c5ce4SKumar Kartikeya Dwivedi struct nlattr *tb[TCA_MAX + 1]; 525715c5ce4SKumar Kartikeya Dwivedi 526715c5ce4SKumar Kartikeya Dwivedi libbpf_nla_parse(tb, TCA_MAX, 527*ee62a5c6SKumar Kartikeya Dwivedi (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))), 528715c5ce4SKumar Kartikeya Dwivedi NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL); 529715c5ce4SKumar Kartikeya Dwivedi if (!tb[TCA_KIND]) 530715c5ce4SKumar Kartikeya Dwivedi return NL_CONT; 531715c5ce4SKumar Kartikeya Dwivedi return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO); 532715c5ce4SKumar Kartikeya Dwivedi } 533715c5ce4SKumar Kartikeya Dwivedi 5340ae64fb6SKumar Kartikeya Dwivedi static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd) 535715c5ce4SKumar Kartikeya Dwivedi { 536715c5ce4SKumar Kartikeya Dwivedi struct bpf_prog_info info = {}; 537715c5ce4SKumar Kartikeya Dwivedi __u32 info_len = sizeof(info); 538715c5ce4SKumar Kartikeya Dwivedi char name[256]; 539715c5ce4SKumar Kartikeya Dwivedi int len, ret; 540715c5ce4SKumar Kartikeya Dwivedi 541715c5ce4SKumar Kartikeya Dwivedi ret = bpf_obj_get_info_by_fd(fd, &info, &info_len); 542715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 543715c5ce4SKumar Kartikeya Dwivedi return ret; 544715c5ce4SKumar Kartikeya Dwivedi 5450ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd)); 546715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 547715c5ce4SKumar Kartikeya Dwivedi return ret; 548715c5ce4SKumar Kartikeya Dwivedi len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id); 549715c5ce4SKumar Kartikeya Dwivedi if (len < 0) 550715c5ce4SKumar Kartikeya Dwivedi return -errno; 551715c5ce4SKumar Kartikeya Dwivedi if (len >= sizeof(name)) 552715c5ce4SKumar Kartikeya Dwivedi return -ENAMETOOLONG; 5530ae64fb6SKumar Kartikeya Dwivedi return nlattr_add(req, TCA_BPF_NAME, name, len + 1); 554715c5ce4SKumar Kartikeya Dwivedi } 555715c5ce4SKumar Kartikeya Dwivedi 556715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 557715c5ce4SKumar Kartikeya Dwivedi { 558715c5ce4SKumar Kartikeya Dwivedi __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags; 559715c5ce4SKumar Kartikeya Dwivedi int ret, ifindex, attach_point, prog_fd; 560715c5ce4SKumar Kartikeya Dwivedi struct bpf_cb_ctx info = {}; 5610ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req; 562715c5ce4SKumar Kartikeya Dwivedi struct nlattr *nla; 563715c5ce4SKumar Kartikeya Dwivedi 564715c5ce4SKumar Kartikeya Dwivedi if (!hook || !opts || 565715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(hook, bpf_tc_hook) || 566715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(opts, bpf_tc_opts)) 567e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 568715c5ce4SKumar Kartikeya Dwivedi 569715c5ce4SKumar Kartikeya Dwivedi ifindex = OPTS_GET(hook, ifindex, 0); 570715c5ce4SKumar Kartikeya Dwivedi parent = OPTS_GET(hook, parent, 0); 571715c5ce4SKumar Kartikeya Dwivedi attach_point = OPTS_GET(hook, attach_point, 0); 572715c5ce4SKumar Kartikeya Dwivedi 573715c5ce4SKumar Kartikeya Dwivedi handle = OPTS_GET(opts, handle, 0); 574715c5ce4SKumar Kartikeya Dwivedi priority = OPTS_GET(opts, priority, 0); 575715c5ce4SKumar Kartikeya Dwivedi prog_fd = OPTS_GET(opts, prog_fd, 0); 576715c5ce4SKumar Kartikeya Dwivedi prog_id = OPTS_GET(opts, prog_id, 0); 577715c5ce4SKumar Kartikeya Dwivedi flags = OPTS_GET(opts, flags, 0); 578715c5ce4SKumar Kartikeya Dwivedi 579715c5ce4SKumar Kartikeya Dwivedi if (ifindex <= 0 || !prog_fd || prog_id) 580e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 581715c5ce4SKumar Kartikeya Dwivedi if (priority > UINT16_MAX) 582e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 583715c5ce4SKumar Kartikeya Dwivedi if (flags & ~BPF_TC_F_REPLACE) 584e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 585715c5ce4SKumar Kartikeya Dwivedi 586715c5ce4SKumar Kartikeya Dwivedi flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL; 587715c5ce4SKumar Kartikeya Dwivedi protocol = ETH_P_ALL; 588715c5ce4SKumar Kartikeya Dwivedi 589715c5ce4SKumar Kartikeya Dwivedi memset(&req, 0, sizeof(req)); 590715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 591715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | 592715c5ce4SKumar Kartikeya Dwivedi NLM_F_ECHO | flags; 593715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_type = RTM_NEWTFILTER; 594715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_family = AF_UNSPEC; 595715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_ifindex = ifindex; 596715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_handle = handle; 597715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 598715c5ce4SKumar Kartikeya Dwivedi 599715c5ce4SKumar Kartikeya Dwivedi ret = tc_get_tcm_parent(attach_point, &parent); 600715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 601e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 602715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_parent = parent; 603715c5ce4SKumar Kartikeya Dwivedi 6040ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 605715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 606e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 6070ae64fb6SKumar Kartikeya Dwivedi nla = nlattr_begin_nested(&req, TCA_OPTIONS); 608715c5ce4SKumar Kartikeya Dwivedi if (!nla) 609e9fc3ce9SAndrii Nakryiko return libbpf_err(-EMSGSIZE); 6100ae64fb6SKumar Kartikeya Dwivedi ret = tc_add_fd_and_name(&req, prog_fd); 611715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 612e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 613715c5ce4SKumar Kartikeya Dwivedi bpf_flags = TCA_BPF_FLAG_ACT_DIRECT; 6140ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags)); 615715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 616e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 6170ae64fb6SKumar Kartikeya Dwivedi nlattr_end_nested(&req, nla); 618715c5ce4SKumar Kartikeya Dwivedi 619715c5ce4SKumar Kartikeya Dwivedi info.opts = opts; 620715c5ce4SKumar Kartikeya Dwivedi 6210ae64fb6SKumar Kartikeya Dwivedi ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info); 622715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 623e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 624715c5ce4SKumar Kartikeya Dwivedi if (!info.processed) 625e9fc3ce9SAndrii Nakryiko return libbpf_err(-ENOENT); 626715c5ce4SKumar Kartikeya Dwivedi return ret; 627715c5ce4SKumar Kartikeya Dwivedi } 628715c5ce4SKumar Kartikeya Dwivedi 629715c5ce4SKumar Kartikeya Dwivedi static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 630715c5ce4SKumar Kartikeya Dwivedi const struct bpf_tc_opts *opts, 631715c5ce4SKumar Kartikeya Dwivedi const bool flush) 632715c5ce4SKumar Kartikeya Dwivedi { 633715c5ce4SKumar Kartikeya Dwivedi __u32 protocol = 0, handle, priority, parent, prog_id, flags; 634715c5ce4SKumar Kartikeya Dwivedi int ret, ifindex, attach_point, prog_fd; 6350ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req; 636715c5ce4SKumar Kartikeya Dwivedi 637715c5ce4SKumar Kartikeya Dwivedi if (!hook || 638715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(hook, bpf_tc_hook) || 639715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(opts, bpf_tc_opts)) 640715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 641715c5ce4SKumar Kartikeya Dwivedi 642715c5ce4SKumar Kartikeya Dwivedi ifindex = OPTS_GET(hook, ifindex, 0); 643715c5ce4SKumar Kartikeya Dwivedi parent = OPTS_GET(hook, parent, 0); 644715c5ce4SKumar Kartikeya Dwivedi attach_point = OPTS_GET(hook, attach_point, 0); 645715c5ce4SKumar Kartikeya Dwivedi 646715c5ce4SKumar Kartikeya Dwivedi handle = OPTS_GET(opts, handle, 0); 647715c5ce4SKumar Kartikeya Dwivedi priority = OPTS_GET(opts, priority, 0); 648715c5ce4SKumar Kartikeya Dwivedi prog_fd = OPTS_GET(opts, prog_fd, 0); 649715c5ce4SKumar Kartikeya Dwivedi prog_id = OPTS_GET(opts, prog_id, 0); 650715c5ce4SKumar Kartikeya Dwivedi flags = OPTS_GET(opts, flags, 0); 651715c5ce4SKumar Kartikeya Dwivedi 652715c5ce4SKumar Kartikeya Dwivedi if (ifindex <= 0 || flags || prog_fd || prog_id) 653715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 654715c5ce4SKumar Kartikeya Dwivedi if (priority > UINT16_MAX) 655715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 656715c5ce4SKumar Kartikeya Dwivedi if (!flush) { 657715c5ce4SKumar Kartikeya Dwivedi if (!handle || !priority) 658715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 659715c5ce4SKumar Kartikeya Dwivedi protocol = ETH_P_ALL; 660715c5ce4SKumar Kartikeya Dwivedi } else { 661715c5ce4SKumar Kartikeya Dwivedi if (handle || priority) 662715c5ce4SKumar Kartikeya Dwivedi return -EINVAL; 663715c5ce4SKumar Kartikeya Dwivedi } 664715c5ce4SKumar Kartikeya Dwivedi 665715c5ce4SKumar Kartikeya Dwivedi memset(&req, 0, sizeof(req)); 666715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 667715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 668715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_type = RTM_DELTFILTER; 669715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_family = AF_UNSPEC; 670715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_ifindex = ifindex; 671715c5ce4SKumar Kartikeya Dwivedi if (!flush) { 672715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_handle = handle; 673715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 674715c5ce4SKumar Kartikeya Dwivedi } 675715c5ce4SKumar Kartikeya Dwivedi 676715c5ce4SKumar Kartikeya Dwivedi ret = tc_get_tcm_parent(attach_point, &parent); 677715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 678715c5ce4SKumar Kartikeya Dwivedi return ret; 679715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_parent = parent; 680715c5ce4SKumar Kartikeya Dwivedi 681715c5ce4SKumar Kartikeya Dwivedi if (!flush) { 6820ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 683715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 684715c5ce4SKumar Kartikeya Dwivedi return ret; 685715c5ce4SKumar Kartikeya Dwivedi } 686715c5ce4SKumar Kartikeya Dwivedi 6870ae64fb6SKumar Kartikeya Dwivedi return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); 688715c5ce4SKumar Kartikeya Dwivedi } 689715c5ce4SKumar Kartikeya Dwivedi 690715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_detach(const struct bpf_tc_hook *hook, 691715c5ce4SKumar Kartikeya Dwivedi const struct bpf_tc_opts *opts) 692715c5ce4SKumar Kartikeya Dwivedi { 693e9fc3ce9SAndrii Nakryiko int ret; 694e9fc3ce9SAndrii Nakryiko 695e9fc3ce9SAndrii Nakryiko if (!opts) 696e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 697e9fc3ce9SAndrii Nakryiko 698e9fc3ce9SAndrii Nakryiko ret = __bpf_tc_detach(hook, opts, false); 699e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 700715c5ce4SKumar Kartikeya Dwivedi } 701715c5ce4SKumar Kartikeya Dwivedi 702715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 703715c5ce4SKumar Kartikeya Dwivedi { 704715c5ce4SKumar Kartikeya Dwivedi __u32 protocol, handle, priority, parent, prog_id, flags; 705715c5ce4SKumar Kartikeya Dwivedi int ret, ifindex, attach_point, prog_fd; 706715c5ce4SKumar Kartikeya Dwivedi struct bpf_cb_ctx info = {}; 7070ae64fb6SKumar Kartikeya Dwivedi struct libbpf_nla_req req; 708715c5ce4SKumar Kartikeya Dwivedi 709715c5ce4SKumar Kartikeya Dwivedi if (!hook || !opts || 710715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(hook, bpf_tc_hook) || 711715c5ce4SKumar Kartikeya Dwivedi !OPTS_VALID(opts, bpf_tc_opts)) 712e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 713715c5ce4SKumar Kartikeya Dwivedi 714715c5ce4SKumar Kartikeya Dwivedi ifindex = OPTS_GET(hook, ifindex, 0); 715715c5ce4SKumar Kartikeya Dwivedi parent = OPTS_GET(hook, parent, 0); 716715c5ce4SKumar Kartikeya Dwivedi attach_point = OPTS_GET(hook, attach_point, 0); 717715c5ce4SKumar Kartikeya Dwivedi 718715c5ce4SKumar Kartikeya Dwivedi handle = OPTS_GET(opts, handle, 0); 719715c5ce4SKumar Kartikeya Dwivedi priority = OPTS_GET(opts, priority, 0); 720715c5ce4SKumar Kartikeya Dwivedi prog_fd = OPTS_GET(opts, prog_fd, 0); 721715c5ce4SKumar Kartikeya Dwivedi prog_id = OPTS_GET(opts, prog_id, 0); 722715c5ce4SKumar Kartikeya Dwivedi flags = OPTS_GET(opts, flags, 0); 723715c5ce4SKumar Kartikeya Dwivedi 724715c5ce4SKumar Kartikeya Dwivedi if (ifindex <= 0 || flags || prog_fd || prog_id || 725715c5ce4SKumar Kartikeya Dwivedi !handle || !priority) 726e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 727715c5ce4SKumar Kartikeya Dwivedi if (priority > UINT16_MAX) 728e9fc3ce9SAndrii Nakryiko return libbpf_err(-EINVAL); 729715c5ce4SKumar Kartikeya Dwivedi 730715c5ce4SKumar Kartikeya Dwivedi protocol = ETH_P_ALL; 731715c5ce4SKumar Kartikeya Dwivedi 732715c5ce4SKumar Kartikeya Dwivedi memset(&req, 0, sizeof(req)); 733715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 734715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_flags = NLM_F_REQUEST; 735715c5ce4SKumar Kartikeya Dwivedi req.nh.nlmsg_type = RTM_GETTFILTER; 736715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_family = AF_UNSPEC; 737715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_ifindex = ifindex; 738715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_handle = handle; 739715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 740715c5ce4SKumar Kartikeya Dwivedi 741715c5ce4SKumar Kartikeya Dwivedi ret = tc_get_tcm_parent(attach_point, &parent); 742715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 743e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 744715c5ce4SKumar Kartikeya Dwivedi req.tc.tcm_parent = parent; 745715c5ce4SKumar Kartikeya Dwivedi 7460ae64fb6SKumar Kartikeya Dwivedi ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 747715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 748e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 749715c5ce4SKumar Kartikeya Dwivedi 750715c5ce4SKumar Kartikeya Dwivedi info.opts = opts; 751715c5ce4SKumar Kartikeya Dwivedi 7520ae64fb6SKumar Kartikeya Dwivedi ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info); 753715c5ce4SKumar Kartikeya Dwivedi if (ret < 0) 754e9fc3ce9SAndrii Nakryiko return libbpf_err(ret); 755715c5ce4SKumar Kartikeya Dwivedi if (!info.processed) 756e9fc3ce9SAndrii Nakryiko return libbpf_err(-ENOENT); 757715c5ce4SKumar Kartikeya Dwivedi return ret; 758715c5ce4SKumar Kartikeya Dwivedi } 759