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