Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
0002 /* Copyright (c) 2018 Facebook */
0003 
0004 #include <stdlib.h>
0005 #include <memory.h>
0006 #include <unistd.h>
0007 #include <arpa/inet.h>
0008 #include <linux/bpf.h>
0009 #include <linux/if_ether.h>
0010 #include <linux/pkt_cls.h>
0011 #include <linux/rtnetlink.h>
0012 #include <sys/socket.h>
0013 #include <errno.h>
0014 #include <time.h>
0015 
0016 #include "bpf.h"
0017 #include "libbpf.h"
0018 #include "libbpf_internal.h"
0019 #include "nlattr.h"
0020 
0021 #ifndef SOL_NETLINK
0022 #define SOL_NETLINK 270
0023 #endif
0024 
0025 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
0026 
0027 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
0028                   void *cookie);
0029 
0030 struct xdp_link_info {
0031     __u32 prog_id;
0032     __u32 drv_prog_id;
0033     __u32 hw_prog_id;
0034     __u32 skb_prog_id;
0035     __u8 attach_mode;
0036 };
0037 
0038 struct xdp_id_md {
0039     int ifindex;
0040     __u32 flags;
0041     struct xdp_link_info info;
0042 };
0043 
0044 static int libbpf_netlink_open(__u32 *nl_pid)
0045 {
0046     struct sockaddr_nl sa;
0047     socklen_t addrlen;
0048     int one = 1, ret;
0049     int sock;
0050 
0051     memset(&sa, 0, sizeof(sa));
0052     sa.nl_family = AF_NETLINK;
0053 
0054     sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
0055     if (sock < 0)
0056         return -errno;
0057 
0058     if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
0059                &one, sizeof(one)) < 0) {
0060         pr_warn("Netlink error reporting not supported\n");
0061     }
0062 
0063     if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
0064         ret = -errno;
0065         goto cleanup;
0066     }
0067 
0068     addrlen = sizeof(sa);
0069     if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
0070         ret = -errno;
0071         goto cleanup;
0072     }
0073 
0074     if (addrlen != sizeof(sa)) {
0075         ret = -LIBBPF_ERRNO__INTERNAL;
0076         goto cleanup;
0077     }
0078 
0079     *nl_pid = sa.nl_pid;
0080     return sock;
0081 
0082 cleanup:
0083     close(sock);
0084     return ret;
0085 }
0086 
0087 static void libbpf_netlink_close(int sock)
0088 {
0089     close(sock);
0090 }
0091 
0092 enum {
0093     NL_CONT,
0094     NL_NEXT,
0095     NL_DONE,
0096 };
0097 
0098 static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
0099 {
0100     int len;
0101 
0102     do {
0103         len = recvmsg(sock, mhdr, flags);
0104     } while (len < 0 && (errno == EINTR || errno == EAGAIN));
0105 
0106     if (len < 0)
0107         return -errno;
0108     return len;
0109 }
0110 
0111 static int alloc_iov(struct iovec *iov, int len)
0112 {
0113     void *nbuf;
0114 
0115     nbuf = realloc(iov->iov_base, len);
0116     if (!nbuf)
0117         return -ENOMEM;
0118 
0119     iov->iov_base = nbuf;
0120     iov->iov_len = len;
0121     return 0;
0122 }
0123 
0124 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
0125                    __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
0126                    void *cookie)
0127 {
0128     struct iovec iov = {};
0129     struct msghdr mhdr = {
0130         .msg_iov = &iov,
0131         .msg_iovlen = 1,
0132     };
0133     bool multipart = true;
0134     struct nlmsgerr *err;
0135     struct nlmsghdr *nh;
0136     int len, ret;
0137 
0138     ret = alloc_iov(&iov, 4096);
0139     if (ret)
0140         goto done;
0141 
0142     while (multipart) {
0143 start:
0144         multipart = false;
0145         len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
0146         if (len < 0) {
0147             ret = len;
0148             goto done;
0149         }
0150 
0151         if (len > iov.iov_len) {
0152             ret = alloc_iov(&iov, len);
0153             if (ret)
0154                 goto done;
0155         }
0156 
0157         len = netlink_recvmsg(sock, &mhdr, 0);
0158         if (len < 0) {
0159             ret = len;
0160             goto done;
0161         }
0162 
0163         if (len == 0)
0164             break;
0165 
0166         for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
0167              nh = NLMSG_NEXT(nh, len)) {
0168             if (nh->nlmsg_pid != nl_pid) {
0169                 ret = -LIBBPF_ERRNO__WRNGPID;
0170                 goto done;
0171             }
0172             if (nh->nlmsg_seq != seq) {
0173                 ret = -LIBBPF_ERRNO__INVSEQ;
0174                 goto done;
0175             }
0176             if (nh->nlmsg_flags & NLM_F_MULTI)
0177                 multipart = true;
0178             switch (nh->nlmsg_type) {
0179             case NLMSG_ERROR:
0180                 err = (struct nlmsgerr *)NLMSG_DATA(nh);
0181                 if (!err->error)
0182                     continue;
0183                 ret = err->error;
0184                 libbpf_nla_dump_errormsg(nh);
0185                 goto done;
0186             case NLMSG_DONE:
0187                 ret = 0;
0188                 goto done;
0189             default:
0190                 break;
0191             }
0192             if (_fn) {
0193                 ret = _fn(nh, fn, cookie);
0194                 switch (ret) {
0195                 case NL_CONT:
0196                     break;
0197                 case NL_NEXT:
0198                     goto start;
0199                 case NL_DONE:
0200                     ret = 0;
0201                     goto done;
0202                 default:
0203                     goto done;
0204                 }
0205             }
0206         }
0207     }
0208     ret = 0;
0209 done:
0210     free(iov.iov_base);
0211     return ret;
0212 }
0213 
0214 static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
0215                     __dump_nlmsg_t parse_msg,
0216                     libbpf_dump_nlmsg_t parse_attr,
0217                     void *cookie)
0218 {
0219     __u32 nl_pid = 0;
0220     int sock, ret;
0221 
0222     sock = libbpf_netlink_open(&nl_pid);
0223     if (sock < 0)
0224         return sock;
0225 
0226     req->nh.nlmsg_pid = 0;
0227     req->nh.nlmsg_seq = time(NULL);
0228 
0229     if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
0230         ret = -errno;
0231         goto out;
0232     }
0233 
0234     ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
0235                   parse_msg, parse_attr, cookie);
0236 out:
0237     libbpf_netlink_close(sock);
0238     return ret;
0239 }
0240 
0241 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
0242                      __u32 flags)
0243 {
0244     struct nlattr *nla;
0245     int ret;
0246     struct libbpf_nla_req req;
0247 
0248     memset(&req, 0, sizeof(req));
0249     req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
0250     req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
0251     req.nh.nlmsg_type     = RTM_SETLINK;
0252     req.ifinfo.ifi_family = AF_UNSPEC;
0253     req.ifinfo.ifi_index  = ifindex;
0254 
0255     nla = nlattr_begin_nested(&req, IFLA_XDP);
0256     if (!nla)
0257         return -EMSGSIZE;
0258     ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
0259     if (ret < 0)
0260         return ret;
0261     if (flags) {
0262         ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
0263         if (ret < 0)
0264             return ret;
0265     }
0266     if (flags & XDP_FLAGS_REPLACE) {
0267         ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
0268                  sizeof(old_fd));
0269         if (ret < 0)
0270             return ret;
0271     }
0272     nlattr_end_nested(&req, nla);
0273 
0274     return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
0275 }
0276 
0277 int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
0278 {
0279     int old_prog_fd, err;
0280 
0281     if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
0282         return libbpf_err(-EINVAL);
0283 
0284     old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
0285     if (old_prog_fd)
0286         flags |= XDP_FLAGS_REPLACE;
0287     else
0288         old_prog_fd = -1;
0289 
0290     err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
0291     return libbpf_err(err);
0292 }
0293 
0294 int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
0295 {
0296     return bpf_xdp_attach(ifindex, -1, flags, opts);
0297 }
0298 
0299 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
0300                  libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
0301 {
0302     struct nlattr *tb[IFLA_MAX + 1], *attr;
0303     struct ifinfomsg *ifi = NLMSG_DATA(nlh);
0304     int len;
0305 
0306     len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
0307     attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
0308 
0309     if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
0310         return -LIBBPF_ERRNO__NLPARSE;
0311 
0312     return dump_link_nlmsg(cookie, ifi, tb);
0313 }
0314 
0315 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
0316 {
0317     struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
0318     struct xdp_id_md *xdp_id = cookie;
0319     struct ifinfomsg *ifinfo = msg;
0320     int ret;
0321 
0322     if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
0323         return 0;
0324 
0325     if (!tb[IFLA_XDP])
0326         return 0;
0327 
0328     ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
0329     if (ret)
0330         return ret;
0331 
0332     if (!xdp_tb[IFLA_XDP_ATTACHED])
0333         return 0;
0334 
0335     xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
0336         xdp_tb[IFLA_XDP_ATTACHED]);
0337 
0338     if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
0339         return 0;
0340 
0341     if (xdp_tb[IFLA_XDP_PROG_ID])
0342         xdp_id->info.prog_id = libbpf_nla_getattr_u32(
0343             xdp_tb[IFLA_XDP_PROG_ID]);
0344 
0345     if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
0346         xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
0347             xdp_tb[IFLA_XDP_SKB_PROG_ID]);
0348 
0349     if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
0350         xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
0351             xdp_tb[IFLA_XDP_DRV_PROG_ID]);
0352 
0353     if (xdp_tb[IFLA_XDP_HW_PROG_ID])
0354         xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
0355             xdp_tb[IFLA_XDP_HW_PROG_ID]);
0356 
0357     return 0;
0358 }
0359 
0360 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
0361 {
0362     struct libbpf_nla_req req = {
0363         .nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
0364         .nh.nlmsg_type     = RTM_GETLINK,
0365         .nh.nlmsg_flags    = NLM_F_DUMP | NLM_F_REQUEST,
0366         .ifinfo.ifi_family = AF_PACKET,
0367     };
0368     struct xdp_id_md xdp_id = {};
0369     int err;
0370 
0371     if (!OPTS_VALID(opts, bpf_xdp_query_opts))
0372         return libbpf_err(-EINVAL);
0373 
0374     if (xdp_flags & ~XDP_FLAGS_MASK)
0375         return libbpf_err(-EINVAL);
0376 
0377     /* Check whether the single {HW,DRV,SKB} mode is set */
0378     xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
0379     if (xdp_flags & (xdp_flags - 1))
0380         return libbpf_err(-EINVAL);
0381 
0382     xdp_id.ifindex = ifindex;
0383     xdp_id.flags = xdp_flags;
0384 
0385     err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,
0386                        get_xdp_info, &xdp_id);
0387     if (err)
0388         return libbpf_err(err);
0389 
0390     OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
0391     OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
0392     OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
0393     OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
0394     OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
0395 
0396     return 0;
0397 }
0398 
0399 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
0400 {
0401     LIBBPF_OPTS(bpf_xdp_query_opts, opts);
0402     int ret;
0403 
0404     ret = bpf_xdp_query(ifindex, flags, &opts);
0405     if (ret)
0406         return libbpf_err(ret);
0407 
0408     flags &= XDP_FLAGS_MODES;
0409 
0410     if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
0411         *prog_id = opts.prog_id;
0412     else if (flags & XDP_FLAGS_DRV_MODE)
0413         *prog_id = opts.drv_prog_id;
0414     else if (flags & XDP_FLAGS_HW_MODE)
0415         *prog_id = opts.hw_prog_id;
0416     else if (flags & XDP_FLAGS_SKB_MODE)
0417         *prog_id = opts.skb_prog_id;
0418     else
0419         *prog_id = 0;
0420 
0421     return 0;
0422 }
0423 
0424 
0425 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
0426 
0427 static int clsact_config(struct libbpf_nla_req *req)
0428 {
0429     req->tc.tcm_parent = TC_H_CLSACT;
0430     req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
0431 
0432     return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
0433 }
0434 
0435 static int attach_point_to_config(struct bpf_tc_hook *hook,
0436                   qdisc_config_t *config)
0437 {
0438     switch (OPTS_GET(hook, attach_point, 0)) {
0439     case BPF_TC_INGRESS:
0440     case BPF_TC_EGRESS:
0441     case BPF_TC_INGRESS | BPF_TC_EGRESS:
0442         if (OPTS_GET(hook, parent, 0))
0443             return -EINVAL;
0444         *config = &clsact_config;
0445         return 0;
0446     case BPF_TC_CUSTOM:
0447         return -EOPNOTSUPP;
0448     default:
0449         return -EINVAL;
0450     }
0451 }
0452 
0453 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
0454                  __u32 *parent)
0455 {
0456     switch (attach_point) {
0457     case BPF_TC_INGRESS:
0458     case BPF_TC_EGRESS:
0459         if (*parent)
0460             return -EINVAL;
0461         *parent = TC_H_MAKE(TC_H_CLSACT,
0462                     attach_point == BPF_TC_INGRESS ?
0463                     TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
0464         break;
0465     case BPF_TC_CUSTOM:
0466         if (!*parent)
0467             return -EINVAL;
0468         break;
0469     default:
0470         return -EINVAL;
0471     }
0472     return 0;
0473 }
0474 
0475 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
0476 {
0477     qdisc_config_t config;
0478     int ret;
0479     struct libbpf_nla_req req;
0480 
0481     ret = attach_point_to_config(hook, &config);
0482     if (ret < 0)
0483         return ret;
0484 
0485     memset(&req, 0, sizeof(req));
0486     req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
0487     req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
0488     req.nh.nlmsg_type  = cmd;
0489     req.tc.tcm_family  = AF_UNSPEC;
0490     req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
0491 
0492     ret = config(&req);
0493     if (ret < 0)
0494         return ret;
0495 
0496     return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
0497 }
0498 
0499 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
0500 {
0501     return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
0502 }
0503 
0504 static int tc_qdisc_delete(struct bpf_tc_hook *hook)
0505 {
0506     return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
0507 }
0508 
0509 int bpf_tc_hook_create(struct bpf_tc_hook *hook)
0510 {
0511     int ret;
0512 
0513     if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
0514         OPTS_GET(hook, ifindex, 0) <= 0)
0515         return libbpf_err(-EINVAL);
0516 
0517     ret = tc_qdisc_create_excl(hook);
0518     return libbpf_err(ret);
0519 }
0520 
0521 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
0522                const struct bpf_tc_opts *opts,
0523                const bool flush);
0524 
0525 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
0526 {
0527     if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
0528         OPTS_GET(hook, ifindex, 0) <= 0)
0529         return libbpf_err(-EINVAL);
0530 
0531     switch (OPTS_GET(hook, attach_point, 0)) {
0532     case BPF_TC_INGRESS:
0533     case BPF_TC_EGRESS:
0534         return libbpf_err(__bpf_tc_detach(hook, NULL, true));
0535     case BPF_TC_INGRESS | BPF_TC_EGRESS:
0536         return libbpf_err(tc_qdisc_delete(hook));
0537     case BPF_TC_CUSTOM:
0538         return libbpf_err(-EOPNOTSUPP);
0539     default:
0540         return libbpf_err(-EINVAL);
0541     }
0542 }
0543 
0544 struct bpf_cb_ctx {
0545     struct bpf_tc_opts *opts;
0546     bool processed;
0547 };
0548 
0549 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
0550              bool unicast)
0551 {
0552     struct nlattr *tbb[TCA_BPF_MAX + 1];
0553     struct bpf_cb_ctx *info = cookie;
0554 
0555     if (!info || !info->opts)
0556         return -EINVAL;
0557     if (unicast && info->processed)
0558         return -EINVAL;
0559     if (!tb[TCA_OPTIONS])
0560         return NL_CONT;
0561 
0562     libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
0563     if (!tbb[TCA_BPF_ID])
0564         return -EINVAL;
0565 
0566     OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
0567     OPTS_SET(info->opts, handle, tc->tcm_handle);
0568     OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
0569 
0570     info->processed = true;
0571     return unicast ? NL_NEXT : NL_DONE;
0572 }
0573 
0574 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
0575                void *cookie)
0576 {
0577     struct tcmsg *tc = NLMSG_DATA(nh);
0578     struct nlattr *tb[TCA_MAX + 1];
0579 
0580     libbpf_nla_parse(tb, TCA_MAX,
0581              (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
0582              NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
0583     if (!tb[TCA_KIND])
0584         return NL_CONT;
0585     return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
0586 }
0587 
0588 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
0589 {
0590     struct bpf_prog_info info = {};
0591     __u32 info_len = sizeof(info);
0592     char name[256];
0593     int len, ret;
0594 
0595     ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
0596     if (ret < 0)
0597         return ret;
0598 
0599     ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
0600     if (ret < 0)
0601         return ret;
0602     len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
0603     if (len < 0)
0604         return -errno;
0605     if (len >= sizeof(name))
0606         return -ENAMETOOLONG;
0607     return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
0608 }
0609 
0610 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
0611 {
0612     __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
0613     int ret, ifindex, attach_point, prog_fd;
0614     struct bpf_cb_ctx info = {};
0615     struct libbpf_nla_req req;
0616     struct nlattr *nla;
0617 
0618     if (!hook || !opts ||
0619         !OPTS_VALID(hook, bpf_tc_hook) ||
0620         !OPTS_VALID(opts, bpf_tc_opts))
0621         return libbpf_err(-EINVAL);
0622 
0623     ifindex      = OPTS_GET(hook, ifindex, 0);
0624     parent       = OPTS_GET(hook, parent, 0);
0625     attach_point = OPTS_GET(hook, attach_point, 0);
0626 
0627     handle       = OPTS_GET(opts, handle, 0);
0628     priority     = OPTS_GET(opts, priority, 0);
0629     prog_fd      = OPTS_GET(opts, prog_fd, 0);
0630     prog_id      = OPTS_GET(opts, prog_id, 0);
0631     flags        = OPTS_GET(opts, flags, 0);
0632 
0633     if (ifindex <= 0 || !prog_fd || prog_id)
0634         return libbpf_err(-EINVAL);
0635     if (priority > UINT16_MAX)
0636         return libbpf_err(-EINVAL);
0637     if (flags & ~BPF_TC_F_REPLACE)
0638         return libbpf_err(-EINVAL);
0639 
0640     flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
0641     protocol = ETH_P_ALL;
0642 
0643     memset(&req, 0, sizeof(req));
0644     req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
0645     req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
0646                  NLM_F_ECHO | flags;
0647     req.nh.nlmsg_type  = RTM_NEWTFILTER;
0648     req.tc.tcm_family  = AF_UNSPEC;
0649     req.tc.tcm_ifindex = ifindex;
0650     req.tc.tcm_handle  = handle;
0651     req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
0652 
0653     ret = tc_get_tcm_parent(attach_point, &parent);
0654     if (ret < 0)
0655         return libbpf_err(ret);
0656     req.tc.tcm_parent = parent;
0657 
0658     ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
0659     if (ret < 0)
0660         return libbpf_err(ret);
0661     nla = nlattr_begin_nested(&req, TCA_OPTIONS);
0662     if (!nla)
0663         return libbpf_err(-EMSGSIZE);
0664     ret = tc_add_fd_and_name(&req, prog_fd);
0665     if (ret < 0)
0666         return libbpf_err(ret);
0667     bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
0668     ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
0669     if (ret < 0)
0670         return libbpf_err(ret);
0671     nlattr_end_nested(&req, nla);
0672 
0673     info.opts = opts;
0674 
0675     ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
0676     if (ret < 0)
0677         return libbpf_err(ret);
0678     if (!info.processed)
0679         return libbpf_err(-ENOENT);
0680     return ret;
0681 }
0682 
0683 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
0684                const struct bpf_tc_opts *opts,
0685                const bool flush)
0686 {
0687     __u32 protocol = 0, handle, priority, parent, prog_id, flags;
0688     int ret, ifindex, attach_point, prog_fd;
0689     struct libbpf_nla_req req;
0690 
0691     if (!hook ||
0692         !OPTS_VALID(hook, bpf_tc_hook) ||
0693         !OPTS_VALID(opts, bpf_tc_opts))
0694         return -EINVAL;
0695 
0696     ifindex      = OPTS_GET(hook, ifindex, 0);
0697     parent       = OPTS_GET(hook, parent, 0);
0698     attach_point = OPTS_GET(hook, attach_point, 0);
0699 
0700     handle       = OPTS_GET(opts, handle, 0);
0701     priority     = OPTS_GET(opts, priority, 0);
0702     prog_fd      = OPTS_GET(opts, prog_fd, 0);
0703     prog_id      = OPTS_GET(opts, prog_id, 0);
0704     flags        = OPTS_GET(opts, flags, 0);
0705 
0706     if (ifindex <= 0 || flags || prog_fd || prog_id)
0707         return -EINVAL;
0708     if (priority > UINT16_MAX)
0709         return -EINVAL;
0710     if (!flush) {
0711         if (!handle || !priority)
0712             return -EINVAL;
0713         protocol = ETH_P_ALL;
0714     } else {
0715         if (handle || priority)
0716             return -EINVAL;
0717     }
0718 
0719     memset(&req, 0, sizeof(req));
0720     req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
0721     req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
0722     req.nh.nlmsg_type  = RTM_DELTFILTER;
0723     req.tc.tcm_family  = AF_UNSPEC;
0724     req.tc.tcm_ifindex = ifindex;
0725     if (!flush) {
0726         req.tc.tcm_handle = handle;
0727         req.tc.tcm_info   = TC_H_MAKE(priority << 16, htons(protocol));
0728     }
0729 
0730     ret = tc_get_tcm_parent(attach_point, &parent);
0731     if (ret < 0)
0732         return ret;
0733     req.tc.tcm_parent = parent;
0734 
0735     if (!flush) {
0736         ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
0737         if (ret < 0)
0738             return ret;
0739     }
0740 
0741     return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
0742 }
0743 
0744 int bpf_tc_detach(const struct bpf_tc_hook *hook,
0745           const struct bpf_tc_opts *opts)
0746 {
0747     int ret;
0748 
0749     if (!opts)
0750         return libbpf_err(-EINVAL);
0751 
0752     ret = __bpf_tc_detach(hook, opts, false);
0753     return libbpf_err(ret);
0754 }
0755 
0756 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
0757 {
0758     __u32 protocol, handle, priority, parent, prog_id, flags;
0759     int ret, ifindex, attach_point, prog_fd;
0760     struct bpf_cb_ctx info = {};
0761     struct libbpf_nla_req req;
0762 
0763     if (!hook || !opts ||
0764         !OPTS_VALID(hook, bpf_tc_hook) ||
0765         !OPTS_VALID(opts, bpf_tc_opts))
0766         return libbpf_err(-EINVAL);
0767 
0768     ifindex      = OPTS_GET(hook, ifindex, 0);
0769     parent       = OPTS_GET(hook, parent, 0);
0770     attach_point = OPTS_GET(hook, attach_point, 0);
0771 
0772     handle       = OPTS_GET(opts, handle, 0);
0773     priority     = OPTS_GET(opts, priority, 0);
0774     prog_fd      = OPTS_GET(opts, prog_fd, 0);
0775     prog_id      = OPTS_GET(opts, prog_id, 0);
0776     flags        = OPTS_GET(opts, flags, 0);
0777 
0778     if (ifindex <= 0 || flags || prog_fd || prog_id ||
0779         !handle || !priority)
0780         return libbpf_err(-EINVAL);
0781     if (priority > UINT16_MAX)
0782         return libbpf_err(-EINVAL);
0783 
0784     protocol = ETH_P_ALL;
0785 
0786     memset(&req, 0, sizeof(req));
0787     req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
0788     req.nh.nlmsg_flags = NLM_F_REQUEST;
0789     req.nh.nlmsg_type  = RTM_GETTFILTER;
0790     req.tc.tcm_family  = AF_UNSPEC;
0791     req.tc.tcm_ifindex = ifindex;
0792     req.tc.tcm_handle  = handle;
0793     req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
0794 
0795     ret = tc_get_tcm_parent(attach_point, &parent);
0796     if (ret < 0)
0797         return libbpf_err(ret);
0798     req.tc.tcm_parent = parent;
0799 
0800     ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
0801     if (ret < 0)
0802         return libbpf_err(ret);
0803 
0804     info.opts = opts;
0805 
0806     ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
0807     if (ret < 0)
0808         return libbpf_err(ret);
0809     if (!info.processed)
0810         return libbpf_err(-ENOENT);
0811     return ret;
0812 }