1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2018 Facebook */
9 #include <linux/if_ether.h>
10 #include <linux/pkt_cls.h>
11 #include <linux/rtnetlink.h>
12 #include <sys/socket.h>
18 #include "libbpf_internal.h"
22 #define SOL_NETLINK 270
25 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
27 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
30 struct xdp_link_info {
41 struct xdp_link_info info;
44 static int libbpf_netlink_open(__u32 *nl_pid)
46 struct sockaddr_nl sa;
51 memset(&sa, 0, sizeof(sa));
52 sa.nl_family = AF_NETLINK;
54 sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
58 if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
59 &one, sizeof(one)) < 0) {
60 pr_warn("Netlink error reporting not supported\n");
63 if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
69 if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
74 if (addrlen != sizeof(sa)) {
75 ret = -LIBBPF_ERRNO__INTERNAL;
87 static void libbpf_netlink_close(int sock)
98 static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
103 len = recvmsg(sock, mhdr, flags);
104 } while (len < 0 && (errno == EINTR || errno == EAGAIN));
111 static int alloc_iov(struct iovec *iov, int len)
115 nbuf = realloc(iov->iov_base, len);
119 iov->iov_base = nbuf;
124 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
125 __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
128 struct iovec iov = {};
129 struct msghdr mhdr = {
133 bool multipart = true;
134 struct nlmsgerr *err;
138 ret = alloc_iov(&iov, 4096);
145 len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
151 if (len > iov.iov_len) {
152 ret = alloc_iov(&iov, len);
157 len = netlink_recvmsg(sock, &mhdr, 0);
166 for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
167 nh = NLMSG_NEXT(nh, len)) {
168 if (nh->nlmsg_pid != nl_pid) {
169 ret = -LIBBPF_ERRNO__WRNGPID;
172 if (nh->nlmsg_seq != seq) {
173 ret = -LIBBPF_ERRNO__INVSEQ;
176 if (nh->nlmsg_flags & NLM_F_MULTI)
178 switch (nh->nlmsg_type) {
180 err = (struct nlmsgerr *)NLMSG_DATA(nh);
184 libbpf_nla_dump_errormsg(nh);
193 ret = _fn(nh, fn, cookie);
214 static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
215 __dump_nlmsg_t parse_msg,
216 libbpf_dump_nlmsg_t parse_attr,
222 sock = libbpf_netlink_open(&nl_pid);
226 req->nh.nlmsg_pid = 0;
227 req->nh.nlmsg_seq = time(NULL);
229 if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
234 ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
235 parse_msg, parse_attr, cookie);
237 libbpf_netlink_close(sock);
241 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
246 struct libbpf_nla_req req;
248 memset(&req, 0, sizeof(req));
249 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
250 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
251 req.nh.nlmsg_type = RTM_SETLINK;
252 req.ifinfo.ifi_family = AF_UNSPEC;
253 req.ifinfo.ifi_index = ifindex;
255 nla = nlattr_begin_nested(&req, IFLA_XDP);
258 ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
262 ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
266 if (flags & XDP_FLAGS_REPLACE) {
267 ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
272 nlattr_end_nested(&req, nla);
274 return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
277 int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
279 int old_prog_fd, err;
281 if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
282 return libbpf_err(-EINVAL);
284 old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
286 flags |= XDP_FLAGS_REPLACE;
290 err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
291 return libbpf_err(err);
294 int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
296 return bpf_xdp_attach(ifindex, -1, flags, opts);
299 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
300 libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
302 struct nlattr *tb[IFLA_MAX + 1], *attr;
303 struct ifinfomsg *ifi = NLMSG_DATA(nlh);
306 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
307 attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
309 if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
310 return -LIBBPF_ERRNO__NLPARSE;
312 return dump_link_nlmsg(cookie, ifi, tb);
315 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
317 struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
318 struct xdp_id_md *xdp_id = cookie;
319 struct ifinfomsg *ifinfo = msg;
322 if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
328 ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
332 if (!xdp_tb[IFLA_XDP_ATTACHED])
335 xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
336 xdp_tb[IFLA_XDP_ATTACHED]);
338 if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
341 if (xdp_tb[IFLA_XDP_PROG_ID])
342 xdp_id->info.prog_id = libbpf_nla_getattr_u32(
343 xdp_tb[IFLA_XDP_PROG_ID]);
345 if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
346 xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
347 xdp_tb[IFLA_XDP_SKB_PROG_ID]);
349 if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
350 xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
351 xdp_tb[IFLA_XDP_DRV_PROG_ID]);
353 if (xdp_tb[IFLA_XDP_HW_PROG_ID])
354 xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
355 xdp_tb[IFLA_XDP_HW_PROG_ID]);
360 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
362 struct libbpf_nla_req req = {
363 .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
364 .nh.nlmsg_type = RTM_GETLINK,
365 .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
366 .ifinfo.ifi_family = AF_PACKET,
368 struct xdp_id_md xdp_id = {};
371 if (!OPTS_VALID(opts, bpf_xdp_query_opts))
372 return libbpf_err(-EINVAL);
374 if (xdp_flags & ~XDP_FLAGS_MASK)
375 return libbpf_err(-EINVAL);
377 /* Check whether the single {HW,DRV,SKB} mode is set */
378 xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
379 if (xdp_flags & (xdp_flags - 1))
380 return libbpf_err(-EINVAL);
382 xdp_id.ifindex = ifindex;
383 xdp_id.flags = xdp_flags;
385 err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,
386 get_xdp_info, &xdp_id);
388 return libbpf_err(err);
390 OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
391 OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
392 OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
393 OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
394 OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
399 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
401 LIBBPF_OPTS(bpf_xdp_query_opts, opts);
404 ret = bpf_xdp_query(ifindex, flags, &opts);
406 return libbpf_err(ret);
408 flags &= XDP_FLAGS_MODES;
410 if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
411 *prog_id = opts.prog_id;
412 else if (flags & XDP_FLAGS_DRV_MODE)
413 *prog_id = opts.drv_prog_id;
414 else if (flags & XDP_FLAGS_HW_MODE)
415 *prog_id = opts.hw_prog_id;
416 else if (flags & XDP_FLAGS_SKB_MODE)
417 *prog_id = opts.skb_prog_id;
425 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
427 static int clsact_config(struct libbpf_nla_req *req)
429 req->tc.tcm_parent = TC_H_CLSACT;
430 req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
432 return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
435 static int attach_point_to_config(struct bpf_tc_hook *hook,
436 qdisc_config_t *config)
438 switch (OPTS_GET(hook, attach_point, 0)) {
441 case BPF_TC_INGRESS | BPF_TC_EGRESS:
442 if (OPTS_GET(hook, parent, 0))
444 *config = &clsact_config;
453 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
456 switch (attach_point) {
461 *parent = TC_H_MAKE(TC_H_CLSACT,
462 attach_point == BPF_TC_INGRESS ?
463 TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
475 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
477 qdisc_config_t config;
479 struct libbpf_nla_req req;
481 ret = attach_point_to_config(hook, &config);
485 memset(&req, 0, sizeof(req));
486 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
487 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
488 req.nh.nlmsg_type = cmd;
489 req.tc.tcm_family = AF_UNSPEC;
490 req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
496 return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
499 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
501 return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
504 static int tc_qdisc_delete(struct bpf_tc_hook *hook)
506 return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
509 int bpf_tc_hook_create(struct bpf_tc_hook *hook)
513 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
514 OPTS_GET(hook, ifindex, 0) <= 0)
515 return libbpf_err(-EINVAL);
517 ret = tc_qdisc_create_excl(hook);
518 return libbpf_err(ret);
521 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
522 const struct bpf_tc_opts *opts,
525 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
527 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
528 OPTS_GET(hook, ifindex, 0) <= 0)
529 return libbpf_err(-EINVAL);
531 switch (OPTS_GET(hook, attach_point, 0)) {
534 return libbpf_err(__bpf_tc_detach(hook, NULL, true));
535 case BPF_TC_INGRESS | BPF_TC_EGRESS:
536 return libbpf_err(tc_qdisc_delete(hook));
538 return libbpf_err(-EOPNOTSUPP);
540 return libbpf_err(-EINVAL);
545 struct bpf_tc_opts *opts;
549 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
552 struct nlattr *tbb[TCA_BPF_MAX + 1];
553 struct bpf_cb_ctx *info = cookie;
555 if (!info || !info->opts)
557 if (unicast && info->processed)
559 if (!tb[TCA_OPTIONS])
562 libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
563 if (!tbb[TCA_BPF_ID])
566 OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
567 OPTS_SET(info->opts, handle, tc->tcm_handle);
568 OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
570 info->processed = true;
571 return unicast ? NL_NEXT : NL_DONE;
574 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
577 struct tcmsg *tc = NLMSG_DATA(nh);
578 struct nlattr *tb[TCA_MAX + 1];
580 libbpf_nla_parse(tb, TCA_MAX,
581 (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
582 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
585 return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
588 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
590 struct bpf_prog_info info;
591 __u32 info_len = sizeof(info);
595 memset(&info, 0, info_len);
596 ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
600 ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
603 len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
606 if (len >= sizeof(name))
607 return -ENAMETOOLONG;
608 return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
611 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
613 __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
614 int ret, ifindex, attach_point, prog_fd;
615 struct bpf_cb_ctx info = {};
616 struct libbpf_nla_req req;
619 if (!hook || !opts ||
620 !OPTS_VALID(hook, bpf_tc_hook) ||
621 !OPTS_VALID(opts, bpf_tc_opts))
622 return libbpf_err(-EINVAL);
624 ifindex = OPTS_GET(hook, ifindex, 0);
625 parent = OPTS_GET(hook, parent, 0);
626 attach_point = OPTS_GET(hook, attach_point, 0);
628 handle = OPTS_GET(opts, handle, 0);
629 priority = OPTS_GET(opts, priority, 0);
630 prog_fd = OPTS_GET(opts, prog_fd, 0);
631 prog_id = OPTS_GET(opts, prog_id, 0);
632 flags = OPTS_GET(opts, flags, 0);
634 if (ifindex <= 0 || !prog_fd || prog_id)
635 return libbpf_err(-EINVAL);
636 if (priority > UINT16_MAX)
637 return libbpf_err(-EINVAL);
638 if (flags & ~BPF_TC_F_REPLACE)
639 return libbpf_err(-EINVAL);
641 flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
642 protocol = ETH_P_ALL;
644 memset(&req, 0, sizeof(req));
645 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
646 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
648 req.nh.nlmsg_type = RTM_NEWTFILTER;
649 req.tc.tcm_family = AF_UNSPEC;
650 req.tc.tcm_ifindex = ifindex;
651 req.tc.tcm_handle = handle;
652 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
654 ret = tc_get_tcm_parent(attach_point, &parent);
656 return libbpf_err(ret);
657 req.tc.tcm_parent = parent;
659 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
661 return libbpf_err(ret);
662 nla = nlattr_begin_nested(&req, TCA_OPTIONS);
664 return libbpf_err(-EMSGSIZE);
665 ret = tc_add_fd_and_name(&req, prog_fd);
667 return libbpf_err(ret);
668 bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
669 ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
671 return libbpf_err(ret);
672 nlattr_end_nested(&req, nla);
676 ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
678 return libbpf_err(ret);
680 return libbpf_err(-ENOENT);
684 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
685 const struct bpf_tc_opts *opts,
688 __u32 protocol = 0, handle, priority, parent, prog_id, flags;
689 int ret, ifindex, attach_point, prog_fd;
690 struct libbpf_nla_req req;
693 !OPTS_VALID(hook, bpf_tc_hook) ||
694 !OPTS_VALID(opts, bpf_tc_opts))
697 ifindex = OPTS_GET(hook, ifindex, 0);
698 parent = OPTS_GET(hook, parent, 0);
699 attach_point = OPTS_GET(hook, attach_point, 0);
701 handle = OPTS_GET(opts, handle, 0);
702 priority = OPTS_GET(opts, priority, 0);
703 prog_fd = OPTS_GET(opts, prog_fd, 0);
704 prog_id = OPTS_GET(opts, prog_id, 0);
705 flags = OPTS_GET(opts, flags, 0);
707 if (ifindex <= 0 || flags || prog_fd || prog_id)
709 if (priority > UINT16_MAX)
712 if (!handle || !priority)
714 protocol = ETH_P_ALL;
716 if (handle || priority)
720 memset(&req, 0, sizeof(req));
721 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
722 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
723 req.nh.nlmsg_type = RTM_DELTFILTER;
724 req.tc.tcm_family = AF_UNSPEC;
725 req.tc.tcm_ifindex = ifindex;
727 req.tc.tcm_handle = handle;
728 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
731 ret = tc_get_tcm_parent(attach_point, &parent);
734 req.tc.tcm_parent = parent;
737 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
742 return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
745 int bpf_tc_detach(const struct bpf_tc_hook *hook,
746 const struct bpf_tc_opts *opts)
751 return libbpf_err(-EINVAL);
753 ret = __bpf_tc_detach(hook, opts, false);
754 return libbpf_err(ret);
757 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
759 __u32 protocol, handle, priority, parent, prog_id, flags;
760 int ret, ifindex, attach_point, prog_fd;
761 struct bpf_cb_ctx info = {};
762 struct libbpf_nla_req req;
764 if (!hook || !opts ||
765 !OPTS_VALID(hook, bpf_tc_hook) ||
766 !OPTS_VALID(opts, bpf_tc_opts))
767 return libbpf_err(-EINVAL);
769 ifindex = OPTS_GET(hook, ifindex, 0);
770 parent = OPTS_GET(hook, parent, 0);
771 attach_point = OPTS_GET(hook, attach_point, 0);
773 handle = OPTS_GET(opts, handle, 0);
774 priority = OPTS_GET(opts, priority, 0);
775 prog_fd = OPTS_GET(opts, prog_fd, 0);
776 prog_id = OPTS_GET(opts, prog_id, 0);
777 flags = OPTS_GET(opts, flags, 0);
779 if (ifindex <= 0 || flags || prog_fd || prog_id ||
780 !handle || !priority)
781 return libbpf_err(-EINVAL);
782 if (priority > UINT16_MAX)
783 return libbpf_err(-EINVAL);
785 protocol = ETH_P_ALL;
787 memset(&req, 0, sizeof(req));
788 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
789 req.nh.nlmsg_flags = NLM_F_REQUEST;
790 req.nh.nlmsg_type = RTM_GETTFILTER;
791 req.tc.tcm_family = AF_UNSPEC;
792 req.tc.tcm_ifindex = ifindex;
793 req.tc.tcm_handle = handle;
794 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol));
796 ret = tc_get_tcm_parent(attach_point, &parent);
798 return libbpf_err(ret);
799 req.tc.tcm_parent = parent;
801 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
803 return libbpf_err(ret);
807 ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
809 return libbpf_err(ret);
811 return libbpf_err(-ENOENT);