xref: /openbmc/linux/tools/lib/bpf/netlink.c (revision fa5bef5e)
11bc38b8fSAlexei Starovoitov // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2f7010770SYonghong Song /* Copyright (c) 2018 Facebook */
3f7010770SYonghong Song 
4f7010770SYonghong Song #include <stdlib.h>
5f7010770SYonghong Song #include <memory.h>
6f7010770SYonghong Song #include <unistd.h>
7715c5ce4SKumar Kartikeya Dwivedi #include <arpa/inet.h>
8f7010770SYonghong Song #include <linux/bpf.h>
9715c5ce4SKumar Kartikeya Dwivedi #include <linux/if_ether.h>
10715c5ce4SKumar Kartikeya Dwivedi #include <linux/pkt_cls.h>
11f7010770SYonghong Song #include <linux/rtnetlink.h>
1204d58f1bSLorenzo Bianconi #include <linux/netdev.h>
13f7010770SYonghong Song #include <sys/socket.h>
14f7010770SYonghong Song #include <errno.h>
15f7010770SYonghong Song #include <time.h>
16f7010770SYonghong Song 
17f7010770SYonghong Song #include "bpf.h"
18f7010770SYonghong Song #include "libbpf.h"
19b6e99b01SToke Høiland-Jørgensen #include "libbpf_internal.h"
20f7010770SYonghong Song #include "nlattr.h"
21f7010770SYonghong Song 
22f7010770SYonghong Song #ifndef SOL_NETLINK
23f7010770SYonghong Song #define SOL_NETLINK 270
24f7010770SYonghong Song #endif
25f7010770SYonghong Song 
267084566aSAndrii Nakryiko typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
277084566aSAndrii Nakryiko 
28aae57780SAndrey Ignatov typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
29434fe9d4SAndrey Ignatov 			      void *cookie);
30434fe9d4SAndrey Ignatov 
3153e6af3aSAndrii Nakryiko struct xdp_link_info {
3253e6af3aSAndrii Nakryiko 	__u32 prog_id;
3353e6af3aSAndrii Nakryiko 	__u32 drv_prog_id;
3453e6af3aSAndrii Nakryiko 	__u32 hw_prog_id;
3553e6af3aSAndrii Nakryiko 	__u32 skb_prog_id;
3653e6af3aSAndrii Nakryiko 	__u8 attach_mode;
3753e6af3aSAndrii Nakryiko };
3853e6af3aSAndrii Nakryiko 
3950db9f07SMaciej Fijalkowski struct xdp_id_md {
4050db9f07SMaciej Fijalkowski 	int ifindex;
4150db9f07SMaciej Fijalkowski 	__u32 flags;
42473f4e13SToke Høiland-Jørgensen 	struct xdp_link_info info;
4304d58f1bSLorenzo Bianconi 	__u64 feature_flags;
4404d58f1bSLorenzo Bianconi };
4504d58f1bSLorenzo Bianconi 
4604d58f1bSLorenzo Bianconi struct xdp_features_md {
4704d58f1bSLorenzo Bianconi 	int ifindex;
4813ce2daaSMaciej Fijalkowski 	__u32 xdp_zc_max_segs;
4904d58f1bSLorenzo Bianconi 	__u64 flags;
5050db9f07SMaciej Fijalkowski };
5150db9f07SMaciej Fijalkowski 
libbpf_netlink_open(__u32 * nl_pid,int proto)528f166931SLorenzo Bianconi static int libbpf_netlink_open(__u32 *nl_pid, int proto)
53f7010770SYonghong Song {
54f7010770SYonghong Song 	struct sockaddr_nl sa;
55f7010770SYonghong Song 	socklen_t addrlen;
56f7010770SYonghong Song 	int one = 1, ret;
57f7010770SYonghong Song 	int sock;
58f7010770SYonghong Song 
59f7010770SYonghong Song 	memset(&sa, 0, sizeof(sa));
60f7010770SYonghong Song 	sa.nl_family = AF_NETLINK;
61f7010770SYonghong Song 
628f166931SLorenzo Bianconi 	sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto);
63f7010770SYonghong Song 	if (sock < 0)
64f7010770SYonghong Song 		return -errno;
65f7010770SYonghong Song 
66f7010770SYonghong Song 	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
67f7010770SYonghong Song 		       &one, sizeof(one)) < 0) {
68b6e99b01SToke Høiland-Jørgensen 		pr_warn("Netlink error reporting not supported\n");
69f7010770SYonghong Song 	}
70f7010770SYonghong Song 
71f7010770SYonghong Song 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
72f7010770SYonghong Song 		ret = -errno;
73f7010770SYonghong Song 		goto cleanup;
74f7010770SYonghong Song 	}
75f7010770SYonghong Song 
76f7010770SYonghong Song 	addrlen = sizeof(sa);
77f7010770SYonghong Song 	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
78f7010770SYonghong Song 		ret = -errno;
79f7010770SYonghong Song 		goto cleanup;
80f7010770SYonghong Song 	}
81f7010770SYonghong Song 
82f7010770SYonghong Song 	if (addrlen != sizeof(sa)) {
83f7010770SYonghong Song 		ret = -LIBBPF_ERRNO__INTERNAL;
84f7010770SYonghong Song 		goto cleanup;
85f7010770SYonghong Song 	}
86f7010770SYonghong Song 
87f7010770SYonghong Song 	*nl_pid = sa.nl_pid;
88f7010770SYonghong Song 	return sock;
89f7010770SYonghong Song 
90f7010770SYonghong Song cleanup:
91f7010770SYonghong Song 	close(sock);
92f7010770SYonghong Song 	return ret;
93f7010770SYonghong Song }
94f7010770SYonghong Song 
libbpf_netlink_close(int sock)958bbb77b7SKumar Kartikeya Dwivedi static void libbpf_netlink_close(int sock)
968bbb77b7SKumar Kartikeya Dwivedi {
978bbb77b7SKumar Kartikeya Dwivedi 	close(sock);
988bbb77b7SKumar Kartikeya Dwivedi }
998bbb77b7SKumar Kartikeya Dwivedi 
100715c5ce4SKumar Kartikeya Dwivedi enum {
101715c5ce4SKumar Kartikeya Dwivedi 	NL_CONT,
102715c5ce4SKumar Kartikeya Dwivedi 	NL_NEXT,
103715c5ce4SKumar Kartikeya Dwivedi 	NL_DONE,
104715c5ce4SKumar Kartikeya Dwivedi };
105715c5ce4SKumar Kartikeya Dwivedi 
netlink_recvmsg(int sock,struct msghdr * mhdr,int flags)1069c3de619SToke Høiland-Jørgensen static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
1079c3de619SToke Høiland-Jørgensen {
1089c3de619SToke Høiland-Jørgensen 	int len;
1099c3de619SToke Høiland-Jørgensen 
1109c3de619SToke Høiland-Jørgensen 	do {
1119c3de619SToke Høiland-Jørgensen 		len = recvmsg(sock, mhdr, flags);
1129c3de619SToke Høiland-Jørgensen 	} while (len < 0 && (errno == EINTR || errno == EAGAIN));
1139c3de619SToke Høiland-Jørgensen 
1149c3de619SToke Høiland-Jørgensen 	if (len < 0)
1159c3de619SToke Høiland-Jørgensen 		return -errno;
1169c3de619SToke Høiland-Jørgensen 	return len;
1179c3de619SToke Høiland-Jørgensen }
1189c3de619SToke Høiland-Jørgensen 
alloc_iov(struct iovec * iov,int len)1199c3de619SToke Høiland-Jørgensen static int alloc_iov(struct iovec *iov, int len)
1209c3de619SToke Høiland-Jørgensen {
1219c3de619SToke Høiland-Jørgensen 	void *nbuf;
1229c3de619SToke Høiland-Jørgensen 
1239c3de619SToke Høiland-Jørgensen 	nbuf = realloc(iov->iov_base, len);
1249c3de619SToke Høiland-Jørgensen 	if (!nbuf)
1259c3de619SToke Høiland-Jørgensen 		return -ENOMEM;
1269c3de619SToke Høiland-Jørgensen 
1279c3de619SToke Høiland-Jørgensen 	iov->iov_base = nbuf;
1289c3de619SToke Høiland-Jørgensen 	iov->iov_len = len;
1299c3de619SToke Høiland-Jørgensen 	return 0;
1309c3de619SToke Høiland-Jørgensen }
1319c3de619SToke Høiland-Jørgensen 
libbpf_netlink_recv(int sock,__u32 nl_pid,int seq,__dump_nlmsg_t _fn,libbpf_dump_nlmsg_t fn,void * cookie)1328bbb77b7SKumar Kartikeya Dwivedi static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
133aae57780SAndrey Ignatov 			       __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
13436f1678dSYonghong Song 			       void *cookie)
135f7010770SYonghong Song {
1369c3de619SToke Høiland-Jørgensen 	struct iovec iov = {};
1379c3de619SToke Høiland-Jørgensen 	struct msghdr mhdr = {
1389c3de619SToke Høiland-Jørgensen 		.msg_iov = &iov,
1399c3de619SToke Høiland-Jørgensen 		.msg_iovlen = 1,
1409c3de619SToke Høiland-Jørgensen 	};
1419d0b3c1fSYonghong Song 	bool multipart = true;
142f7010770SYonghong Song 	struct nlmsgerr *err;
143f7010770SYonghong Song 	struct nlmsghdr *nh;
144f7010770SYonghong Song 	int len, ret;
145f7010770SYonghong Song 
1469c3de619SToke Høiland-Jørgensen 	ret = alloc_iov(&iov, 4096);
1479c3de619SToke Høiland-Jørgensen 	if (ret)
1489c3de619SToke Høiland-Jørgensen 		goto done;
1499c3de619SToke Høiland-Jørgensen 
1509d0b3c1fSYonghong Song 	while (multipart) {
151715c5ce4SKumar Kartikeya Dwivedi start:
1529d0b3c1fSYonghong Song 		multipart = false;
1539c3de619SToke Høiland-Jørgensen 		len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
154f7010770SYonghong Song 		if (len < 0) {
1559c3de619SToke Høiland-Jørgensen 			ret = len;
1569c3de619SToke Høiland-Jørgensen 			goto done;
1579c3de619SToke Høiland-Jørgensen 		}
1589c3de619SToke Høiland-Jørgensen 
1599c3de619SToke Høiland-Jørgensen 		if (len > iov.iov_len) {
1609c3de619SToke Høiland-Jørgensen 			ret = alloc_iov(&iov, len);
1619c3de619SToke Høiland-Jørgensen 			if (ret)
1629c3de619SToke Høiland-Jørgensen 				goto done;
1639c3de619SToke Høiland-Jørgensen 		}
1649c3de619SToke Høiland-Jørgensen 
1659c3de619SToke Høiland-Jørgensen 		len = netlink_recvmsg(sock, &mhdr, 0);
1669c3de619SToke Høiland-Jørgensen 		if (len < 0) {
1679c3de619SToke Høiland-Jørgensen 			ret = len;
168f7010770SYonghong Song 			goto done;
169f7010770SYonghong Song 		}
170f7010770SYonghong Song 
1719d0b3c1fSYonghong Song 		if (len == 0)
1729d0b3c1fSYonghong Song 			break;
1739d0b3c1fSYonghong Song 
1749c3de619SToke Høiland-Jørgensen 		for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
175f7010770SYonghong Song 		     nh = NLMSG_NEXT(nh, len)) {
176f7010770SYonghong Song 			if (nh->nlmsg_pid != nl_pid) {
177f7010770SYonghong Song 				ret = -LIBBPF_ERRNO__WRNGPID;
178f7010770SYonghong Song 				goto done;
179f7010770SYonghong Song 			}
180f7010770SYonghong Song 			if (nh->nlmsg_seq != seq) {
181f7010770SYonghong Song 				ret = -LIBBPF_ERRNO__INVSEQ;
182f7010770SYonghong Song 				goto done;
183f7010770SYonghong Song 			}
1849d0b3c1fSYonghong Song 			if (nh->nlmsg_flags & NLM_F_MULTI)
1859d0b3c1fSYonghong Song 				multipart = true;
186f7010770SYonghong Song 			switch (nh->nlmsg_type) {
187f7010770SYonghong Song 			case NLMSG_ERROR:
188f7010770SYonghong Song 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
189f7010770SYonghong Song 				if (!err->error)
190f7010770SYonghong Song 					continue;
191f7010770SYonghong Song 				ret = err->error;
192f04bc8a4SAndrey Ignatov 				libbpf_nla_dump_errormsg(nh);
193f7010770SYonghong Song 				goto done;
194f7010770SYonghong Song 			case NLMSG_DONE:
1951b8c924aSAndrii Nakryiko 				ret = 0;
1961b8c924aSAndrii Nakryiko 				goto done;
197f7010770SYonghong Song 			default:
198f7010770SYonghong Song 				break;
199f7010770SYonghong Song 			}
20036f1678dSYonghong Song 			if (_fn) {
20136f1678dSYonghong Song 				ret = _fn(nh, fn, cookie);
202715c5ce4SKumar Kartikeya Dwivedi 				switch (ret) {
203715c5ce4SKumar Kartikeya Dwivedi 				case NL_CONT:
204715c5ce4SKumar Kartikeya Dwivedi 					break;
205715c5ce4SKumar Kartikeya Dwivedi 				case NL_NEXT:
206715c5ce4SKumar Kartikeya Dwivedi 					goto start;
207715c5ce4SKumar Kartikeya Dwivedi 				case NL_DONE:
2081b8c924aSAndrii Nakryiko 					ret = 0;
2091b8c924aSAndrii Nakryiko 					goto done;
210715c5ce4SKumar Kartikeya Dwivedi 				default:
2111b8c924aSAndrii Nakryiko 					goto done;
21236f1678dSYonghong Song 				}
213f7010770SYonghong Song 			}
214f7010770SYonghong Song 		}
215715c5ce4SKumar Kartikeya Dwivedi 	}
216f7010770SYonghong Song 	ret = 0;
217f7010770SYonghong Song done:
2189c3de619SToke Høiland-Jørgensen 	free(iov.iov_base);
219f7010770SYonghong Song 	return ret;
220f7010770SYonghong Song }
221f7010770SYonghong Song 
libbpf_netlink_send_recv(struct libbpf_nla_req * req,int proto,__dump_nlmsg_t parse_msg,libbpf_dump_nlmsg_t parse_attr,void * cookie)2220ae64fb6SKumar Kartikeya Dwivedi static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
2238f166931SLorenzo Bianconi 				    int proto, __dump_nlmsg_t parse_msg,
2248bbb77b7SKumar Kartikeya Dwivedi 				    libbpf_dump_nlmsg_t parse_attr,
2258bbb77b7SKumar Kartikeya Dwivedi 				    void *cookie)
226f7010770SYonghong Song {
2274734b0feSJeremy Cline 	__u32 nl_pid = 0;
2288bbb77b7SKumar Kartikeya Dwivedi 	int sock, ret;
229f7010770SYonghong Song 
2308f166931SLorenzo Bianconi 	sock = libbpf_netlink_open(&nl_pid, proto);
231f7010770SYonghong Song 	if (sock < 0)
232f7010770SYonghong Song 		return sock;
233f7010770SYonghong Song 
2340ae64fb6SKumar Kartikeya Dwivedi 	req->nh.nlmsg_pid = 0;
2350ae64fb6SKumar Kartikeya Dwivedi 	req->nh.nlmsg_seq = time(NULL);
2368bbb77b7SKumar Kartikeya Dwivedi 
2370ae64fb6SKumar Kartikeya Dwivedi 	if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
2388bbb77b7SKumar Kartikeya Dwivedi 		ret = -errno;
2398bbb77b7SKumar Kartikeya Dwivedi 		goto out;
2408bbb77b7SKumar Kartikeya Dwivedi 	}
2418bbb77b7SKumar Kartikeya Dwivedi 
2420ae64fb6SKumar Kartikeya Dwivedi 	ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
2438bbb77b7SKumar Kartikeya Dwivedi 				  parse_msg, parse_attr, cookie);
2448bbb77b7SKumar Kartikeya Dwivedi out:
2458bbb77b7SKumar Kartikeya Dwivedi 	libbpf_netlink_close(sock);
2468bbb77b7SKumar Kartikeya Dwivedi 	return ret;
2478bbb77b7SKumar Kartikeya Dwivedi }
2488bbb77b7SKumar Kartikeya Dwivedi 
parse_genl_family_id(struct nlmsghdr * nh,libbpf_dump_nlmsg_t fn,void * cookie)24904d58f1bSLorenzo Bianconi static int parse_genl_family_id(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
25004d58f1bSLorenzo Bianconi 				void *cookie)
25104d58f1bSLorenzo Bianconi {
25204d58f1bSLorenzo Bianconi 	struct genlmsghdr *gnl = NLMSG_DATA(nh);
25304d58f1bSLorenzo Bianconi 	struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
25404d58f1bSLorenzo Bianconi 	struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
25504d58f1bSLorenzo Bianconi 	__u16 *id = cookie;
25604d58f1bSLorenzo Bianconi 
25704d58f1bSLorenzo Bianconi 	libbpf_nla_parse(tb, CTRL_ATTR_FAMILY_ID, na,
25804d58f1bSLorenzo Bianconi 			 NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
25904d58f1bSLorenzo Bianconi 	if (!tb[CTRL_ATTR_FAMILY_ID])
26004d58f1bSLorenzo Bianconi 		return NL_CONT;
26104d58f1bSLorenzo Bianconi 
26204d58f1bSLorenzo Bianconi 	*id = libbpf_nla_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
26304d58f1bSLorenzo Bianconi 	return NL_DONE;
26404d58f1bSLorenzo Bianconi }
26504d58f1bSLorenzo Bianconi 
libbpf_netlink_resolve_genl_family_id(const char * name,__u16 len,__u16 * id)26604d58f1bSLorenzo Bianconi static int libbpf_netlink_resolve_genl_family_id(const char *name,
26704d58f1bSLorenzo Bianconi 						 __u16 len, __u16 *id)
26804d58f1bSLorenzo Bianconi {
26904d58f1bSLorenzo Bianconi 	struct libbpf_nla_req req = {
27004d58f1bSLorenzo Bianconi 		.nh.nlmsg_len	= NLMSG_LENGTH(GENL_HDRLEN),
27104d58f1bSLorenzo Bianconi 		.nh.nlmsg_type	= GENL_ID_CTRL,
27204d58f1bSLorenzo Bianconi 		.nh.nlmsg_flags	= NLM_F_REQUEST,
27304d58f1bSLorenzo Bianconi 		.gnl.cmd	= CTRL_CMD_GETFAMILY,
27404d58f1bSLorenzo Bianconi 		.gnl.version	= 2,
27504d58f1bSLorenzo Bianconi 	};
27604d58f1bSLorenzo Bianconi 	int err;
27704d58f1bSLorenzo Bianconi 
27804d58f1bSLorenzo Bianconi 	err = nlattr_add(&req, CTRL_ATTR_FAMILY_NAME, name, len);
27904d58f1bSLorenzo Bianconi 	if (err < 0)
28004d58f1bSLorenzo Bianconi 		return err;
28104d58f1bSLorenzo Bianconi 
28204d58f1bSLorenzo Bianconi 	return libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
28304d58f1bSLorenzo Bianconi 					parse_genl_family_id, NULL, id);
28404d58f1bSLorenzo Bianconi }
28504d58f1bSLorenzo Bianconi 
__bpf_set_link_xdp_fd_replace(int ifindex,int fd,int old_fd,__u32 flags)2868bbb77b7SKumar Kartikeya Dwivedi static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
2878bbb77b7SKumar Kartikeya Dwivedi 					 __u32 flags)
2888bbb77b7SKumar Kartikeya Dwivedi {
2898bbb77b7SKumar Kartikeya Dwivedi 	struct nlattr *nla;
2908bbb77b7SKumar Kartikeya Dwivedi 	int ret;
2910ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req;
2928bbb77b7SKumar Kartikeya Dwivedi 
293f7010770SYonghong Song 	memset(&req, 0, sizeof(req));
294f7010770SYonghong Song 	req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
295f7010770SYonghong Song 	req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
296f7010770SYonghong Song 	req.nh.nlmsg_type     = RTM_SETLINK;
297f7010770SYonghong Song 	req.ifinfo.ifi_family = AF_UNSPEC;
298f7010770SYonghong Song 	req.ifinfo.ifi_index  = ifindex;
299f7010770SYonghong Song 
3000ae64fb6SKumar Kartikeya Dwivedi 	nla = nlattr_begin_nested(&req, IFLA_XDP);
3018bbb77b7SKumar Kartikeya Dwivedi 	if (!nla)
3028bbb77b7SKumar Kartikeya Dwivedi 		return -EMSGSIZE;
3030ae64fb6SKumar Kartikeya Dwivedi 	ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
3048bbb77b7SKumar Kartikeya Dwivedi 	if (ret < 0)
305f7010770SYonghong Song 		return ret;
3068bbb77b7SKumar Kartikeya Dwivedi 	if (flags) {
3070ae64fb6SKumar Kartikeya Dwivedi 		ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
3088bbb77b7SKumar Kartikeya Dwivedi 		if (ret < 0)
3098bbb77b7SKumar Kartikeya Dwivedi 			return ret;
3108bbb77b7SKumar Kartikeya Dwivedi 	}
3118bbb77b7SKumar Kartikeya Dwivedi 	if (flags & XDP_FLAGS_REPLACE) {
3120ae64fb6SKumar Kartikeya Dwivedi 		ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
3130ae64fb6SKumar Kartikeya Dwivedi 				 sizeof(old_fd));
3148bbb77b7SKumar Kartikeya Dwivedi 		if (ret < 0)
3158bbb77b7SKumar Kartikeya Dwivedi 			return ret;
3168bbb77b7SKumar Kartikeya Dwivedi 	}
3170ae64fb6SKumar Kartikeya Dwivedi 	nlattr_end_nested(&req, nla);
3188bbb77b7SKumar Kartikeya Dwivedi 
3198f166931SLorenzo Bianconi 	return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
320f7010770SYonghong Song }
32136f1678dSYonghong Song 
bpf_xdp_attach(int ifindex,int prog_fd,__u32 flags,const struct bpf_xdp_attach_opts * opts)322c359821aSAndrii Nakryiko int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
323c359821aSAndrii Nakryiko {
324c359821aSAndrii Nakryiko 	int old_prog_fd, err;
325c359821aSAndrii Nakryiko 
326c359821aSAndrii Nakryiko 	if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
327c359821aSAndrii Nakryiko 		return libbpf_err(-EINVAL);
328c359821aSAndrii Nakryiko 
329c359821aSAndrii Nakryiko 	old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
330c359821aSAndrii Nakryiko 	if (old_prog_fd)
331c359821aSAndrii Nakryiko 		flags |= XDP_FLAGS_REPLACE;
332c359821aSAndrii Nakryiko 	else
333c359821aSAndrii Nakryiko 		old_prog_fd = -1;
334c359821aSAndrii Nakryiko 
335c359821aSAndrii Nakryiko 	err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
336c359821aSAndrii Nakryiko 	return libbpf_err(err);
337c359821aSAndrii Nakryiko }
338c359821aSAndrii Nakryiko 
bpf_xdp_detach(int ifindex,__u32 flags,const struct bpf_xdp_attach_opts * opts)339c359821aSAndrii Nakryiko int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
340c359821aSAndrii Nakryiko {
341c359821aSAndrii Nakryiko 	return bpf_xdp_attach(ifindex, -1, flags, opts);
342c359821aSAndrii Nakryiko }
343c359821aSAndrii Nakryiko 
__dump_link_nlmsg(struct nlmsghdr * nlh,libbpf_dump_nlmsg_t dump_link_nlmsg,void * cookie)344aae57780SAndrey Ignatov static int __dump_link_nlmsg(struct nlmsghdr *nlh,
345aae57780SAndrey Ignatov 			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
34636f1678dSYonghong Song {
34736f1678dSYonghong Song 	struct nlattr *tb[IFLA_MAX + 1], *attr;
34836f1678dSYonghong Song 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
34936f1678dSYonghong Song 	int len;
35036f1678dSYonghong Song 
35136f1678dSYonghong Song 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
35236f1678dSYonghong Song 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
3538bbb77b7SKumar Kartikeya Dwivedi 
354f04bc8a4SAndrey Ignatov 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
35536f1678dSYonghong Song 		return -LIBBPF_ERRNO__NLPARSE;
35636f1678dSYonghong Song 
35736f1678dSYonghong Song 	return dump_link_nlmsg(cookie, ifi, tb);
35836f1678dSYonghong Song }
35936f1678dSYonghong Song 
get_xdp_info(void * cookie,void * msg,struct nlattr ** tb)360473f4e13SToke Høiland-Jørgensen static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
36150db9f07SMaciej Fijalkowski {
36250db9f07SMaciej Fijalkowski 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
36350db9f07SMaciej Fijalkowski 	struct xdp_id_md *xdp_id = cookie;
36450db9f07SMaciej Fijalkowski 	struct ifinfomsg *ifinfo = msg;
36550db9f07SMaciej Fijalkowski 	int ret;
36650db9f07SMaciej Fijalkowski 
36750db9f07SMaciej Fijalkowski 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
36850db9f07SMaciej Fijalkowski 		return 0;
36950db9f07SMaciej Fijalkowski 
37050db9f07SMaciej Fijalkowski 	if (!tb[IFLA_XDP])
37150db9f07SMaciej Fijalkowski 		return 0;
37250db9f07SMaciej Fijalkowski 
37350db9f07SMaciej Fijalkowski 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
37450db9f07SMaciej Fijalkowski 	if (ret)
37550db9f07SMaciej Fijalkowski 		return ret;
37650db9f07SMaciej Fijalkowski 
37750db9f07SMaciej Fijalkowski 	if (!xdp_tb[IFLA_XDP_ATTACHED])
37850db9f07SMaciej Fijalkowski 		return 0;
37950db9f07SMaciej Fijalkowski 
380473f4e13SToke Høiland-Jørgensen 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
381473f4e13SToke Høiland-Jørgensen 		xdp_tb[IFLA_XDP_ATTACHED]);
382473f4e13SToke Høiland-Jørgensen 
383473f4e13SToke Høiland-Jørgensen 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
38450db9f07SMaciej Fijalkowski 		return 0;
38550db9f07SMaciej Fijalkowski 
386473f4e13SToke Høiland-Jørgensen 	if (xdp_tb[IFLA_XDP_PROG_ID])
387473f4e13SToke Høiland-Jørgensen 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
388473f4e13SToke Høiland-Jørgensen 			xdp_tb[IFLA_XDP_PROG_ID]);
38950db9f07SMaciej Fijalkowski 
390473f4e13SToke Høiland-Jørgensen 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
391473f4e13SToke Høiland-Jørgensen 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
392473f4e13SToke Høiland-Jørgensen 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
393473f4e13SToke Høiland-Jørgensen 
394473f4e13SToke Høiland-Jørgensen 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
395473f4e13SToke Høiland-Jørgensen 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
396473f4e13SToke Høiland-Jørgensen 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
397473f4e13SToke Høiland-Jørgensen 
398473f4e13SToke Høiland-Jørgensen 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
399473f4e13SToke Høiland-Jørgensen 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
400473f4e13SToke Høiland-Jørgensen 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
40150db9f07SMaciej Fijalkowski 
40250db9f07SMaciej Fijalkowski 	return 0;
40350db9f07SMaciej Fijalkowski }
40450db9f07SMaciej Fijalkowski 
parse_xdp_features(struct nlmsghdr * nh,libbpf_dump_nlmsg_t fn,void * cookie)40504d58f1bSLorenzo Bianconi static int parse_xdp_features(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
40604d58f1bSLorenzo Bianconi 			      void *cookie)
40704d58f1bSLorenzo Bianconi {
40804d58f1bSLorenzo Bianconi 	struct genlmsghdr *gnl = NLMSG_DATA(nh);
40904d58f1bSLorenzo Bianconi 	struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
41004d58f1bSLorenzo Bianconi 	struct nlattr *tb[NETDEV_CMD_MAX + 1];
41104d58f1bSLorenzo Bianconi 	struct xdp_features_md *md = cookie;
41204d58f1bSLorenzo Bianconi 	__u32 ifindex;
41304d58f1bSLorenzo Bianconi 
41404d58f1bSLorenzo Bianconi 	libbpf_nla_parse(tb, NETDEV_CMD_MAX, na,
41504d58f1bSLorenzo Bianconi 			 NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
41604d58f1bSLorenzo Bianconi 
41704d58f1bSLorenzo Bianconi 	if (!tb[NETDEV_A_DEV_IFINDEX] || !tb[NETDEV_A_DEV_XDP_FEATURES])
41804d58f1bSLorenzo Bianconi 		return NL_CONT;
41904d58f1bSLorenzo Bianconi 
42004d58f1bSLorenzo Bianconi 	ifindex = libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_IFINDEX]);
42104d58f1bSLorenzo Bianconi 	if (ifindex != md->ifindex)
42204d58f1bSLorenzo Bianconi 		return NL_CONT;
42304d58f1bSLorenzo Bianconi 
42404d58f1bSLorenzo Bianconi 	md->flags = libbpf_nla_getattr_u64(tb[NETDEV_A_DEV_XDP_FEATURES]);
42513ce2daaSMaciej Fijalkowski 	if (tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS])
42613ce2daaSMaciej Fijalkowski 		md->xdp_zc_max_segs =
42713ce2daaSMaciej Fijalkowski 			libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_XDP_ZC_MAX_SEGS]);
42804d58f1bSLorenzo Bianconi 	return NL_DONE;
42904d58f1bSLorenzo Bianconi }
43004d58f1bSLorenzo Bianconi 
bpf_xdp_query(int ifindex,int xdp_flags,struct bpf_xdp_query_opts * opts)431c359821aSAndrii Nakryiko int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
43250db9f07SMaciej Fijalkowski {
4330ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req = {
4348bbb77b7SKumar Kartikeya Dwivedi 		.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
4358bbb77b7SKumar Kartikeya Dwivedi 		.nh.nlmsg_type     = RTM_GETLINK,
4368bbb77b7SKumar Kartikeya Dwivedi 		.nh.nlmsg_flags    = NLM_F_DUMP | NLM_F_REQUEST,
4370ae64fb6SKumar Kartikeya Dwivedi 		.ifinfo.ifi_family = AF_PACKET,
4388bbb77b7SKumar Kartikeya Dwivedi 	};
439c359821aSAndrii Nakryiko 	struct xdp_id_md xdp_id = {};
44004d58f1bSLorenzo Bianconi 	struct xdp_features_md md = {
44104d58f1bSLorenzo Bianconi 		.ifindex = ifindex,
44204d58f1bSLorenzo Bianconi 	};
44304d58f1bSLorenzo Bianconi 	__u16 id;
444c359821aSAndrii Nakryiko 	int err;
44550db9f07SMaciej Fijalkowski 
446c359821aSAndrii Nakryiko 	if (!OPTS_VALID(opts, bpf_xdp_query_opts))
447c359821aSAndrii Nakryiko 		return libbpf_err(-EINVAL);
448c359821aSAndrii Nakryiko 
449c359821aSAndrii Nakryiko 	if (xdp_flags & ~XDP_FLAGS_MASK)
450e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
45150db9f07SMaciej Fijalkowski 
45250db9f07SMaciej Fijalkowski 	/* Check whether the single {HW,DRV,SKB} mode is set */
453c359821aSAndrii Nakryiko 	xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
454c359821aSAndrii Nakryiko 	if (xdp_flags & (xdp_flags - 1))
455e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
45650db9f07SMaciej Fijalkowski 
45750db9f07SMaciej Fijalkowski 	xdp_id.ifindex = ifindex;
458c359821aSAndrii Nakryiko 	xdp_id.flags = xdp_flags;
45950db9f07SMaciej Fijalkowski 
4608f166931SLorenzo Bianconi 	err = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, __dump_link_nlmsg,
4618bbb77b7SKumar Kartikeya Dwivedi 				       get_xdp_info, &xdp_id);
462c359821aSAndrii Nakryiko 	if (err)
463c359821aSAndrii Nakryiko 		return libbpf_err(err);
464473f4e13SToke Høiland-Jørgensen 
465c359821aSAndrii Nakryiko 	OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
466c359821aSAndrii Nakryiko 	OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
467c359821aSAndrii Nakryiko 	OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
468c359821aSAndrii Nakryiko 	OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
469c359821aSAndrii Nakryiko 	OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
470473f4e13SToke Høiland-Jørgensen 
47104d58f1bSLorenzo Bianconi 	if (!OPTS_HAS(opts, feature_flags))
47204d58f1bSLorenzo Bianconi 		return 0;
47304d58f1bSLorenzo Bianconi 
47404d58f1bSLorenzo Bianconi 	err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
475c8ee37bdSYonghong Song 	if (err < 0) {
476c8ee37bdSYonghong Song 		if (err == -ENOENT) {
477c8ee37bdSYonghong Song 			opts->feature_flags = 0;
478c8ee37bdSYonghong Song 			goto skip_feature_flags;
479c8ee37bdSYonghong Song 		}
48004d58f1bSLorenzo Bianconi 		return libbpf_err(err);
481c8ee37bdSYonghong Song 	}
48204d58f1bSLorenzo Bianconi 
48304d58f1bSLorenzo Bianconi 	memset(&req, 0, sizeof(req));
48404d58f1bSLorenzo Bianconi 	req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
48504d58f1bSLorenzo Bianconi 	req.nh.nlmsg_flags = NLM_F_REQUEST;
48604d58f1bSLorenzo Bianconi 	req.nh.nlmsg_type = id;
48704d58f1bSLorenzo Bianconi 	req.gnl.cmd = NETDEV_CMD_DEV_GET;
48804d58f1bSLorenzo Bianconi 	req.gnl.version = 2;
48904d58f1bSLorenzo Bianconi 
49004d58f1bSLorenzo Bianconi 	err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
49104d58f1bSLorenzo Bianconi 	if (err < 0)
49202fc0e73SLorenzo Bianconi 		return libbpf_err(err);
49304d58f1bSLorenzo Bianconi 
49404d58f1bSLorenzo Bianconi 	err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
49504d58f1bSLorenzo Bianconi 				       parse_xdp_features, NULL, &md);
49604d58f1bSLorenzo Bianconi 	if (err)
49704d58f1bSLorenzo Bianconi 		return libbpf_err(err);
49804d58f1bSLorenzo Bianconi 
499*fa5bef5eSToke Høiland-Jørgensen 	OPTS_SET(opts, feature_flags, md.flags);
500*fa5bef5eSToke Høiland-Jørgensen 	OPTS_SET(opts, xdp_zc_max_segs, md.xdp_zc_max_segs);
50104d58f1bSLorenzo Bianconi 
502c8ee37bdSYonghong Song skip_feature_flags:
503473f4e13SToke Høiland-Jørgensen 	return 0;
504473f4e13SToke Høiland-Jørgensen }
505473f4e13SToke Høiland-Jørgensen 
bpf_xdp_query_id(int ifindex,int flags,__u32 * prog_id)506c359821aSAndrii Nakryiko int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
507c359821aSAndrii Nakryiko {
508c359821aSAndrii Nakryiko 	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
509473f4e13SToke Høiland-Jørgensen 	int ret;
510473f4e13SToke Høiland-Jørgensen 
511c359821aSAndrii Nakryiko 	ret = bpf_xdp_query(ifindex, flags, &opts);
512c359821aSAndrii Nakryiko 	if (ret)
513e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
514c359821aSAndrii Nakryiko 
515c359821aSAndrii Nakryiko 	flags &= XDP_FLAGS_MODES;
516c359821aSAndrii Nakryiko 
517c359821aSAndrii Nakryiko 	if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
518c359821aSAndrii Nakryiko 		*prog_id = opts.prog_id;
519c359821aSAndrii Nakryiko 	else if (flags & XDP_FLAGS_DRV_MODE)
520c359821aSAndrii Nakryiko 		*prog_id = opts.drv_prog_id;
521c359821aSAndrii Nakryiko 	else if (flags & XDP_FLAGS_HW_MODE)
522c359821aSAndrii Nakryiko 		*prog_id = opts.hw_prog_id;
523c359821aSAndrii Nakryiko 	else if (flags & XDP_FLAGS_SKB_MODE)
524c359821aSAndrii Nakryiko 		*prog_id = opts.skb_prog_id;
525c359821aSAndrii Nakryiko 	else
526c359821aSAndrii Nakryiko 		*prog_id = 0;
527c359821aSAndrii Nakryiko 
528c359821aSAndrii Nakryiko 	return 0;
529c359821aSAndrii Nakryiko }
530c359821aSAndrii Nakryiko 
531c359821aSAndrii Nakryiko 
5320ae64fb6SKumar Kartikeya Dwivedi typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
533715c5ce4SKumar Kartikeya Dwivedi 
clsact_config(struct libbpf_nla_req * req)5340ae64fb6SKumar Kartikeya Dwivedi static int clsact_config(struct libbpf_nla_req *req)
535715c5ce4SKumar Kartikeya Dwivedi {
5360ae64fb6SKumar Kartikeya Dwivedi 	req->tc.tcm_parent = TC_H_CLSACT;
5370ae64fb6SKumar Kartikeya Dwivedi 	req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
538715c5ce4SKumar Kartikeya Dwivedi 
5390ae64fb6SKumar Kartikeya Dwivedi 	return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
540715c5ce4SKumar Kartikeya Dwivedi }
541715c5ce4SKumar Kartikeya Dwivedi 
attach_point_to_config(struct bpf_tc_hook * hook,qdisc_config_t * config)542715c5ce4SKumar Kartikeya Dwivedi static int attach_point_to_config(struct bpf_tc_hook *hook,
543715c5ce4SKumar Kartikeya Dwivedi 				  qdisc_config_t *config)
544715c5ce4SKumar Kartikeya Dwivedi {
545715c5ce4SKumar Kartikeya Dwivedi 	switch (OPTS_GET(hook, attach_point, 0)) {
546715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_INGRESS:
547715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_EGRESS:
548715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
549715c5ce4SKumar Kartikeya Dwivedi 		if (OPTS_GET(hook, parent, 0))
550715c5ce4SKumar Kartikeya Dwivedi 			return -EINVAL;
551715c5ce4SKumar Kartikeya Dwivedi 		*config = &clsact_config;
552715c5ce4SKumar Kartikeya Dwivedi 		return 0;
553715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_CUSTOM:
554715c5ce4SKumar Kartikeya Dwivedi 		return -EOPNOTSUPP;
555715c5ce4SKumar Kartikeya Dwivedi 	default:
556715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
557715c5ce4SKumar Kartikeya Dwivedi 	}
558715c5ce4SKumar Kartikeya Dwivedi }
559715c5ce4SKumar Kartikeya Dwivedi 
tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,__u32 * parent)560715c5ce4SKumar Kartikeya Dwivedi static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
561715c5ce4SKumar Kartikeya Dwivedi 			     __u32 *parent)
562715c5ce4SKumar Kartikeya Dwivedi {
563715c5ce4SKumar Kartikeya Dwivedi 	switch (attach_point) {
564715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_INGRESS:
565715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_EGRESS:
566715c5ce4SKumar Kartikeya Dwivedi 		if (*parent)
567715c5ce4SKumar Kartikeya Dwivedi 			return -EINVAL;
568715c5ce4SKumar Kartikeya Dwivedi 		*parent = TC_H_MAKE(TC_H_CLSACT,
569715c5ce4SKumar Kartikeya Dwivedi 				    attach_point == BPF_TC_INGRESS ?
570715c5ce4SKumar Kartikeya Dwivedi 				    TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
571715c5ce4SKumar Kartikeya Dwivedi 		break;
572715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_CUSTOM:
573715c5ce4SKumar Kartikeya Dwivedi 		if (!*parent)
574715c5ce4SKumar Kartikeya Dwivedi 			return -EINVAL;
575715c5ce4SKumar Kartikeya Dwivedi 		break;
576715c5ce4SKumar Kartikeya Dwivedi 	default:
577715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
578715c5ce4SKumar Kartikeya Dwivedi 	}
579715c5ce4SKumar Kartikeya Dwivedi 	return 0;
580715c5ce4SKumar Kartikeya Dwivedi }
581715c5ce4SKumar Kartikeya Dwivedi 
tc_qdisc_modify(struct bpf_tc_hook * hook,int cmd,int flags)582715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
583715c5ce4SKumar Kartikeya Dwivedi {
584715c5ce4SKumar Kartikeya Dwivedi 	qdisc_config_t config;
585715c5ce4SKumar Kartikeya Dwivedi 	int ret;
5860ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req;
587715c5ce4SKumar Kartikeya Dwivedi 
588715c5ce4SKumar Kartikeya Dwivedi 	ret = attach_point_to_config(hook, &config);
589715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
590715c5ce4SKumar Kartikeya Dwivedi 		return ret;
591715c5ce4SKumar Kartikeya Dwivedi 
592715c5ce4SKumar Kartikeya Dwivedi 	memset(&req, 0, sizeof(req));
593715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
594715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
595715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_type  = cmd;
596715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_family  = AF_UNSPEC;
597715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
598715c5ce4SKumar Kartikeya Dwivedi 
5990ae64fb6SKumar Kartikeya Dwivedi 	ret = config(&req);
600715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
601715c5ce4SKumar Kartikeya Dwivedi 		return ret;
602715c5ce4SKumar Kartikeya Dwivedi 
6038f166931SLorenzo Bianconi 	return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
604715c5ce4SKumar Kartikeya Dwivedi }
605715c5ce4SKumar Kartikeya Dwivedi 
tc_qdisc_create_excl(struct bpf_tc_hook * hook)606715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
607715c5ce4SKumar Kartikeya Dwivedi {
608bbf29d3aSKumar Kartikeya Dwivedi 	return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
609715c5ce4SKumar Kartikeya Dwivedi }
610715c5ce4SKumar Kartikeya Dwivedi 
tc_qdisc_delete(struct bpf_tc_hook * hook)611715c5ce4SKumar Kartikeya Dwivedi static int tc_qdisc_delete(struct bpf_tc_hook *hook)
612715c5ce4SKumar Kartikeya Dwivedi {
613715c5ce4SKumar Kartikeya Dwivedi 	return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
614715c5ce4SKumar Kartikeya Dwivedi }
615715c5ce4SKumar Kartikeya Dwivedi 
bpf_tc_hook_create(struct bpf_tc_hook * hook)616715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_hook_create(struct bpf_tc_hook *hook)
617715c5ce4SKumar Kartikeya Dwivedi {
618e9fc3ce9SAndrii Nakryiko 	int ret;
619e9fc3ce9SAndrii Nakryiko 
620715c5ce4SKumar Kartikeya Dwivedi 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
621715c5ce4SKumar Kartikeya Dwivedi 	    OPTS_GET(hook, ifindex, 0) <= 0)
622e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
623715c5ce4SKumar Kartikeya Dwivedi 
624e9fc3ce9SAndrii Nakryiko 	ret = tc_qdisc_create_excl(hook);
625e9fc3ce9SAndrii Nakryiko 	return libbpf_err(ret);
626715c5ce4SKumar Kartikeya Dwivedi }
627715c5ce4SKumar Kartikeya Dwivedi 
628715c5ce4SKumar Kartikeya Dwivedi static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
629715c5ce4SKumar Kartikeya Dwivedi 			   const struct bpf_tc_opts *opts,
630715c5ce4SKumar Kartikeya Dwivedi 			   const bool flush);
631715c5ce4SKumar Kartikeya Dwivedi 
bpf_tc_hook_destroy(struct bpf_tc_hook * hook)632715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
633715c5ce4SKumar Kartikeya Dwivedi {
634715c5ce4SKumar Kartikeya Dwivedi 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
635715c5ce4SKumar Kartikeya Dwivedi 	    OPTS_GET(hook, ifindex, 0) <= 0)
636e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
637715c5ce4SKumar Kartikeya Dwivedi 
638715c5ce4SKumar Kartikeya Dwivedi 	switch (OPTS_GET(hook, attach_point, 0)) {
639715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_INGRESS:
640715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_EGRESS:
641e9fc3ce9SAndrii Nakryiko 		return libbpf_err(__bpf_tc_detach(hook, NULL, true));
642715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
643e9fc3ce9SAndrii Nakryiko 		return libbpf_err(tc_qdisc_delete(hook));
644715c5ce4SKumar Kartikeya Dwivedi 	case BPF_TC_CUSTOM:
645e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EOPNOTSUPP);
646715c5ce4SKumar Kartikeya Dwivedi 	default:
647e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
648715c5ce4SKumar Kartikeya Dwivedi 	}
649715c5ce4SKumar Kartikeya Dwivedi }
650715c5ce4SKumar Kartikeya Dwivedi 
651715c5ce4SKumar Kartikeya Dwivedi struct bpf_cb_ctx {
652715c5ce4SKumar Kartikeya Dwivedi 	struct bpf_tc_opts *opts;
653715c5ce4SKumar Kartikeya Dwivedi 	bool processed;
654715c5ce4SKumar Kartikeya Dwivedi };
655715c5ce4SKumar Kartikeya Dwivedi 
__get_tc_info(void * cookie,struct tcmsg * tc,struct nlattr ** tb,bool unicast)656715c5ce4SKumar Kartikeya Dwivedi static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
657715c5ce4SKumar Kartikeya Dwivedi 			 bool unicast)
658715c5ce4SKumar Kartikeya Dwivedi {
659715c5ce4SKumar Kartikeya Dwivedi 	struct nlattr *tbb[TCA_BPF_MAX + 1];
660715c5ce4SKumar Kartikeya Dwivedi 	struct bpf_cb_ctx *info = cookie;
661715c5ce4SKumar Kartikeya Dwivedi 
662715c5ce4SKumar Kartikeya Dwivedi 	if (!info || !info->opts)
663715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
664715c5ce4SKumar Kartikeya Dwivedi 	if (unicast && info->processed)
665715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
666715c5ce4SKumar Kartikeya Dwivedi 	if (!tb[TCA_OPTIONS])
667715c5ce4SKumar Kartikeya Dwivedi 		return NL_CONT;
668715c5ce4SKumar Kartikeya Dwivedi 
669715c5ce4SKumar Kartikeya Dwivedi 	libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
670715c5ce4SKumar Kartikeya Dwivedi 	if (!tbb[TCA_BPF_ID])
671715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
672715c5ce4SKumar Kartikeya Dwivedi 
673715c5ce4SKumar Kartikeya Dwivedi 	OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
674715c5ce4SKumar Kartikeya Dwivedi 	OPTS_SET(info->opts, handle, tc->tcm_handle);
675715c5ce4SKumar Kartikeya Dwivedi 	OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
676715c5ce4SKumar Kartikeya Dwivedi 
677715c5ce4SKumar Kartikeya Dwivedi 	info->processed = true;
678715c5ce4SKumar Kartikeya Dwivedi 	return unicast ? NL_NEXT : NL_DONE;
679715c5ce4SKumar Kartikeya Dwivedi }
680715c5ce4SKumar Kartikeya Dwivedi 
get_tc_info(struct nlmsghdr * nh,libbpf_dump_nlmsg_t fn,void * cookie)681715c5ce4SKumar Kartikeya Dwivedi static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
682715c5ce4SKumar Kartikeya Dwivedi 		       void *cookie)
683715c5ce4SKumar Kartikeya Dwivedi {
684715c5ce4SKumar Kartikeya Dwivedi 	struct tcmsg *tc = NLMSG_DATA(nh);
685715c5ce4SKumar Kartikeya Dwivedi 	struct nlattr *tb[TCA_MAX + 1];
686715c5ce4SKumar Kartikeya Dwivedi 
687715c5ce4SKumar Kartikeya Dwivedi 	libbpf_nla_parse(tb, TCA_MAX,
688ee62a5c6SKumar Kartikeya Dwivedi 			 (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
689715c5ce4SKumar Kartikeya Dwivedi 			 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
690715c5ce4SKumar Kartikeya Dwivedi 	if (!tb[TCA_KIND])
691715c5ce4SKumar Kartikeya Dwivedi 		return NL_CONT;
692715c5ce4SKumar Kartikeya Dwivedi 	return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
693715c5ce4SKumar Kartikeya Dwivedi }
694715c5ce4SKumar Kartikeya Dwivedi 
tc_add_fd_and_name(struct libbpf_nla_req * req,int fd)6950ae64fb6SKumar Kartikeya Dwivedi static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
696715c5ce4SKumar Kartikeya Dwivedi {
697813847a3SAndrii Nakryiko 	struct bpf_prog_info info;
698715c5ce4SKumar Kartikeya Dwivedi 	__u32 info_len = sizeof(info);
699715c5ce4SKumar Kartikeya Dwivedi 	char name[256];
700715c5ce4SKumar Kartikeya Dwivedi 	int len, ret;
701715c5ce4SKumar Kartikeya Dwivedi 
702813847a3SAndrii Nakryiko 	memset(&info, 0, info_len);
703629dfc66SIlya Leoshkevich 	ret = bpf_prog_get_info_by_fd(fd, &info, &info_len);
704715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
705715c5ce4SKumar Kartikeya Dwivedi 		return ret;
706715c5ce4SKumar Kartikeya Dwivedi 
7070ae64fb6SKumar Kartikeya Dwivedi 	ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
708715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
709715c5ce4SKumar Kartikeya Dwivedi 		return ret;
710715c5ce4SKumar Kartikeya Dwivedi 	len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
711715c5ce4SKumar Kartikeya Dwivedi 	if (len < 0)
712715c5ce4SKumar Kartikeya Dwivedi 		return -errno;
713715c5ce4SKumar Kartikeya Dwivedi 	if (len >= sizeof(name))
714715c5ce4SKumar Kartikeya Dwivedi 		return -ENAMETOOLONG;
7150ae64fb6SKumar Kartikeya Dwivedi 	return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
716715c5ce4SKumar Kartikeya Dwivedi }
717715c5ce4SKumar Kartikeya Dwivedi 
bpf_tc_attach(const struct bpf_tc_hook * hook,struct bpf_tc_opts * opts)718715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
719715c5ce4SKumar Kartikeya Dwivedi {
720715c5ce4SKumar Kartikeya Dwivedi 	__u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
721715c5ce4SKumar Kartikeya Dwivedi 	int ret, ifindex, attach_point, prog_fd;
722715c5ce4SKumar Kartikeya Dwivedi 	struct bpf_cb_ctx info = {};
7230ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req;
724715c5ce4SKumar Kartikeya Dwivedi 	struct nlattr *nla;
725715c5ce4SKumar Kartikeya Dwivedi 
726715c5ce4SKumar Kartikeya Dwivedi 	if (!hook || !opts ||
727715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(hook, bpf_tc_hook) ||
728715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(opts, bpf_tc_opts))
729e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
730715c5ce4SKumar Kartikeya Dwivedi 
731715c5ce4SKumar Kartikeya Dwivedi 	ifindex      = OPTS_GET(hook, ifindex, 0);
732715c5ce4SKumar Kartikeya Dwivedi 	parent       = OPTS_GET(hook, parent, 0);
733715c5ce4SKumar Kartikeya Dwivedi 	attach_point = OPTS_GET(hook, attach_point, 0);
734715c5ce4SKumar Kartikeya Dwivedi 
735715c5ce4SKumar Kartikeya Dwivedi 	handle       = OPTS_GET(opts, handle, 0);
736715c5ce4SKumar Kartikeya Dwivedi 	priority     = OPTS_GET(opts, priority, 0);
737715c5ce4SKumar Kartikeya Dwivedi 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
738715c5ce4SKumar Kartikeya Dwivedi 	prog_id      = OPTS_GET(opts, prog_id, 0);
739715c5ce4SKumar Kartikeya Dwivedi 	flags        = OPTS_GET(opts, flags, 0);
740715c5ce4SKumar Kartikeya Dwivedi 
741715c5ce4SKumar Kartikeya Dwivedi 	if (ifindex <= 0 || !prog_fd || prog_id)
742e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
743715c5ce4SKumar Kartikeya Dwivedi 	if (priority > UINT16_MAX)
744e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
745715c5ce4SKumar Kartikeya Dwivedi 	if (flags & ~BPF_TC_F_REPLACE)
746e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
747715c5ce4SKumar Kartikeya Dwivedi 
748715c5ce4SKumar Kartikeya Dwivedi 	flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
749715c5ce4SKumar Kartikeya Dwivedi 	protocol = ETH_P_ALL;
750715c5ce4SKumar Kartikeya Dwivedi 
751715c5ce4SKumar Kartikeya Dwivedi 	memset(&req, 0, sizeof(req));
752715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
753715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
754715c5ce4SKumar Kartikeya Dwivedi 			     NLM_F_ECHO | flags;
755715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_type  = RTM_NEWTFILTER;
756715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_family  = AF_UNSPEC;
757715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_ifindex = ifindex;
758715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_handle  = handle;
759715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
760715c5ce4SKumar Kartikeya Dwivedi 
761715c5ce4SKumar Kartikeya Dwivedi 	ret = tc_get_tcm_parent(attach_point, &parent);
762715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
763e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
764715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_parent = parent;
765715c5ce4SKumar Kartikeya Dwivedi 
7660ae64fb6SKumar Kartikeya Dwivedi 	ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
767715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
768e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
7690ae64fb6SKumar Kartikeya Dwivedi 	nla = nlattr_begin_nested(&req, TCA_OPTIONS);
770715c5ce4SKumar Kartikeya Dwivedi 	if (!nla)
771e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EMSGSIZE);
7720ae64fb6SKumar Kartikeya Dwivedi 	ret = tc_add_fd_and_name(&req, prog_fd);
773715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
774e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
775715c5ce4SKumar Kartikeya Dwivedi 	bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
7760ae64fb6SKumar Kartikeya Dwivedi 	ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
777715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
778e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
7790ae64fb6SKumar Kartikeya Dwivedi 	nlattr_end_nested(&req, nla);
780715c5ce4SKumar Kartikeya Dwivedi 
781715c5ce4SKumar Kartikeya Dwivedi 	info.opts = opts;
782715c5ce4SKumar Kartikeya Dwivedi 
7838f166931SLorenzo Bianconi 	ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
7848f166931SLorenzo Bianconi 				       &info);
785715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
786e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
787715c5ce4SKumar Kartikeya Dwivedi 	if (!info.processed)
788e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-ENOENT);
789715c5ce4SKumar Kartikeya Dwivedi 	return ret;
790715c5ce4SKumar Kartikeya Dwivedi }
791715c5ce4SKumar Kartikeya Dwivedi 
__bpf_tc_detach(const struct bpf_tc_hook * hook,const struct bpf_tc_opts * opts,const bool flush)792715c5ce4SKumar Kartikeya Dwivedi static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
793715c5ce4SKumar Kartikeya Dwivedi 			   const struct bpf_tc_opts *opts,
794715c5ce4SKumar Kartikeya Dwivedi 			   const bool flush)
795715c5ce4SKumar Kartikeya Dwivedi {
796715c5ce4SKumar Kartikeya Dwivedi 	__u32 protocol = 0, handle, priority, parent, prog_id, flags;
797715c5ce4SKumar Kartikeya Dwivedi 	int ret, ifindex, attach_point, prog_fd;
7980ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req;
799715c5ce4SKumar Kartikeya Dwivedi 
800715c5ce4SKumar Kartikeya Dwivedi 	if (!hook ||
801715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(hook, bpf_tc_hook) ||
802715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(opts, bpf_tc_opts))
803715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
804715c5ce4SKumar Kartikeya Dwivedi 
805715c5ce4SKumar Kartikeya Dwivedi 	ifindex      = OPTS_GET(hook, ifindex, 0);
806715c5ce4SKumar Kartikeya Dwivedi 	parent       = OPTS_GET(hook, parent, 0);
807715c5ce4SKumar Kartikeya Dwivedi 	attach_point = OPTS_GET(hook, attach_point, 0);
808715c5ce4SKumar Kartikeya Dwivedi 
809715c5ce4SKumar Kartikeya Dwivedi 	handle       = OPTS_GET(opts, handle, 0);
810715c5ce4SKumar Kartikeya Dwivedi 	priority     = OPTS_GET(opts, priority, 0);
811715c5ce4SKumar Kartikeya Dwivedi 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
812715c5ce4SKumar Kartikeya Dwivedi 	prog_id      = OPTS_GET(opts, prog_id, 0);
813715c5ce4SKumar Kartikeya Dwivedi 	flags        = OPTS_GET(opts, flags, 0);
814715c5ce4SKumar Kartikeya Dwivedi 
815715c5ce4SKumar Kartikeya Dwivedi 	if (ifindex <= 0 || flags || prog_fd || prog_id)
816715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
817715c5ce4SKumar Kartikeya Dwivedi 	if (priority > UINT16_MAX)
818715c5ce4SKumar Kartikeya Dwivedi 		return -EINVAL;
819715c5ce4SKumar Kartikeya Dwivedi 	if (!flush) {
820715c5ce4SKumar Kartikeya Dwivedi 		if (!handle || !priority)
821715c5ce4SKumar Kartikeya Dwivedi 			return -EINVAL;
822715c5ce4SKumar Kartikeya Dwivedi 		protocol = ETH_P_ALL;
823715c5ce4SKumar Kartikeya Dwivedi 	} else {
824715c5ce4SKumar Kartikeya Dwivedi 		if (handle || priority)
825715c5ce4SKumar Kartikeya Dwivedi 			return -EINVAL;
826715c5ce4SKumar Kartikeya Dwivedi 	}
827715c5ce4SKumar Kartikeya Dwivedi 
828715c5ce4SKumar Kartikeya Dwivedi 	memset(&req, 0, sizeof(req));
829715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
830715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
831715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_type  = RTM_DELTFILTER;
832715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_family  = AF_UNSPEC;
833715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_ifindex = ifindex;
834715c5ce4SKumar Kartikeya Dwivedi 	if (!flush) {
835715c5ce4SKumar Kartikeya Dwivedi 		req.tc.tcm_handle = handle;
836715c5ce4SKumar Kartikeya Dwivedi 		req.tc.tcm_info   = TC_H_MAKE(priority << 16, htons(protocol));
837715c5ce4SKumar Kartikeya Dwivedi 	}
838715c5ce4SKumar Kartikeya Dwivedi 
839715c5ce4SKumar Kartikeya Dwivedi 	ret = tc_get_tcm_parent(attach_point, &parent);
840715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
841715c5ce4SKumar Kartikeya Dwivedi 		return ret;
842715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_parent = parent;
843715c5ce4SKumar Kartikeya Dwivedi 
844715c5ce4SKumar Kartikeya Dwivedi 	if (!flush) {
8450ae64fb6SKumar Kartikeya Dwivedi 		ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
846715c5ce4SKumar Kartikeya Dwivedi 		if (ret < 0)
847715c5ce4SKumar Kartikeya Dwivedi 			return ret;
848715c5ce4SKumar Kartikeya Dwivedi 	}
849715c5ce4SKumar Kartikeya Dwivedi 
8508f166931SLorenzo Bianconi 	return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
851715c5ce4SKumar Kartikeya Dwivedi }
852715c5ce4SKumar Kartikeya Dwivedi 
bpf_tc_detach(const struct bpf_tc_hook * hook,const struct bpf_tc_opts * opts)853715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_detach(const struct bpf_tc_hook *hook,
854715c5ce4SKumar Kartikeya Dwivedi 		  const struct bpf_tc_opts *opts)
855715c5ce4SKumar Kartikeya Dwivedi {
856e9fc3ce9SAndrii Nakryiko 	int ret;
857e9fc3ce9SAndrii Nakryiko 
858e9fc3ce9SAndrii Nakryiko 	if (!opts)
859e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
860e9fc3ce9SAndrii Nakryiko 
861e9fc3ce9SAndrii Nakryiko 	ret = __bpf_tc_detach(hook, opts, false);
862e9fc3ce9SAndrii Nakryiko 	return libbpf_err(ret);
863715c5ce4SKumar Kartikeya Dwivedi }
864715c5ce4SKumar Kartikeya Dwivedi 
bpf_tc_query(const struct bpf_tc_hook * hook,struct bpf_tc_opts * opts)865715c5ce4SKumar Kartikeya Dwivedi int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
866715c5ce4SKumar Kartikeya Dwivedi {
867715c5ce4SKumar Kartikeya Dwivedi 	__u32 protocol, handle, priority, parent, prog_id, flags;
868715c5ce4SKumar Kartikeya Dwivedi 	int ret, ifindex, attach_point, prog_fd;
869715c5ce4SKumar Kartikeya Dwivedi 	struct bpf_cb_ctx info = {};
8700ae64fb6SKumar Kartikeya Dwivedi 	struct libbpf_nla_req req;
871715c5ce4SKumar Kartikeya Dwivedi 
872715c5ce4SKumar Kartikeya Dwivedi 	if (!hook || !opts ||
873715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(hook, bpf_tc_hook) ||
874715c5ce4SKumar Kartikeya Dwivedi 	    !OPTS_VALID(opts, bpf_tc_opts))
875e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
876715c5ce4SKumar Kartikeya Dwivedi 
877715c5ce4SKumar Kartikeya Dwivedi 	ifindex      = OPTS_GET(hook, ifindex, 0);
878715c5ce4SKumar Kartikeya Dwivedi 	parent       = OPTS_GET(hook, parent, 0);
879715c5ce4SKumar Kartikeya Dwivedi 	attach_point = OPTS_GET(hook, attach_point, 0);
880715c5ce4SKumar Kartikeya Dwivedi 
881715c5ce4SKumar Kartikeya Dwivedi 	handle       = OPTS_GET(opts, handle, 0);
882715c5ce4SKumar Kartikeya Dwivedi 	priority     = OPTS_GET(opts, priority, 0);
883715c5ce4SKumar Kartikeya Dwivedi 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
884715c5ce4SKumar Kartikeya Dwivedi 	prog_id      = OPTS_GET(opts, prog_id, 0);
885715c5ce4SKumar Kartikeya Dwivedi 	flags        = OPTS_GET(opts, flags, 0);
886715c5ce4SKumar Kartikeya Dwivedi 
887715c5ce4SKumar Kartikeya Dwivedi 	if (ifindex <= 0 || flags || prog_fd || prog_id ||
888715c5ce4SKumar Kartikeya Dwivedi 	    !handle || !priority)
889e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
890715c5ce4SKumar Kartikeya Dwivedi 	if (priority > UINT16_MAX)
891e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-EINVAL);
892715c5ce4SKumar Kartikeya Dwivedi 
893715c5ce4SKumar Kartikeya Dwivedi 	protocol = ETH_P_ALL;
894715c5ce4SKumar Kartikeya Dwivedi 
895715c5ce4SKumar Kartikeya Dwivedi 	memset(&req, 0, sizeof(req));
896715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
897715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_flags = NLM_F_REQUEST;
898715c5ce4SKumar Kartikeya Dwivedi 	req.nh.nlmsg_type  = RTM_GETTFILTER;
899715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_family  = AF_UNSPEC;
900715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_ifindex = ifindex;
901715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_handle  = handle;
902715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
903715c5ce4SKumar Kartikeya Dwivedi 
904715c5ce4SKumar Kartikeya Dwivedi 	ret = tc_get_tcm_parent(attach_point, &parent);
905715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
906e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
907715c5ce4SKumar Kartikeya Dwivedi 	req.tc.tcm_parent = parent;
908715c5ce4SKumar Kartikeya Dwivedi 
9090ae64fb6SKumar Kartikeya Dwivedi 	ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
910715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
911e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
912715c5ce4SKumar Kartikeya Dwivedi 
913715c5ce4SKumar Kartikeya Dwivedi 	info.opts = opts;
914715c5ce4SKumar Kartikeya Dwivedi 
9158f166931SLorenzo Bianconi 	ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
9168f166931SLorenzo Bianconi 				       &info);
917715c5ce4SKumar Kartikeya Dwivedi 	if (ret < 0)
918e9fc3ce9SAndrii Nakryiko 		return libbpf_err(ret);
919715c5ce4SKumar Kartikeya Dwivedi 	if (!info.processed)
920e9fc3ce9SAndrii Nakryiko 		return libbpf_err(-ENOENT);
921715c5ce4SKumar Kartikeya Dwivedi 	return ret;
922715c5ce4SKumar Kartikeya Dwivedi }
923