0001
0002
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
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 }