xref: /openbmc/linux/tools/lib/bpf/netlink.c (revision f8a11425075ff11b4b5784f077cb84f3d2dfb3f0)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2018 Facebook */
3 
4 #include <stdlib.h>
5 #include <memory.h>
6 #include <unistd.h>
7 #include <arpa/inet.h>
8 #include <linux/bpf.h>
9 #include <linux/if_ether.h>
10 #include <linux/pkt_cls.h>
11 #include <linux/rtnetlink.h>
12 #include <sys/socket.h>
13 #include <errno.h>
14 #include <time.h>
15 
16 #include "bpf.h"
17 #include "libbpf.h"
18 #include "libbpf_internal.h"
19 #include "nlattr.h"
20 
21 #ifndef SOL_NETLINK
22 #define SOL_NETLINK 270
23 #endif
24 
25 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
26 
27 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
28 			      void *cookie);
29 
30 struct xdp_id_md {
31 	int ifindex;
32 	__u32 flags;
33 	struct xdp_link_info info;
34 };
35 
36 static int libbpf_netlink_open(__u32 *nl_pid)
37 {
38 	struct sockaddr_nl sa;
39 	socklen_t addrlen;
40 	int one = 1, ret;
41 	int sock;
42 
43 	memset(&sa, 0, sizeof(sa));
44 	sa.nl_family = AF_NETLINK;
45 
46 	sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
47 	if (sock < 0)
48 		return -errno;
49 
50 	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
51 		       &one, sizeof(one)) < 0) {
52 		pr_warn("Netlink error reporting not supported\n");
53 	}
54 
55 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
56 		ret = -errno;
57 		goto cleanup;
58 	}
59 
60 	addrlen = sizeof(sa);
61 	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
62 		ret = -errno;
63 		goto cleanup;
64 	}
65 
66 	if (addrlen != sizeof(sa)) {
67 		ret = -LIBBPF_ERRNO__INTERNAL;
68 		goto cleanup;
69 	}
70 
71 	*nl_pid = sa.nl_pid;
72 	return sock;
73 
74 cleanup:
75 	close(sock);
76 	return ret;
77 }
78 
79 static void libbpf_netlink_close(int sock)
80 {
81 	close(sock);
82 }
83 
84 enum {
85 	NL_CONT,
86 	NL_NEXT,
87 	NL_DONE,
88 };
89 
90 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
91 			       __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
92 			       void *cookie)
93 {
94 	bool multipart = true;
95 	struct nlmsgerr *err;
96 	struct nlmsghdr *nh;
97 	char buf[4096];
98 	int len, ret;
99 
100 	while (multipart) {
101 start:
102 		multipart = false;
103 		len = recv(sock, buf, sizeof(buf), 0);
104 		if (len < 0) {
105 			ret = -errno;
106 			goto done;
107 		}
108 
109 		if (len == 0)
110 			break;
111 
112 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
113 		     nh = NLMSG_NEXT(nh, len)) {
114 			if (nh->nlmsg_pid != nl_pid) {
115 				ret = -LIBBPF_ERRNO__WRNGPID;
116 				goto done;
117 			}
118 			if (nh->nlmsg_seq != seq) {
119 				ret = -LIBBPF_ERRNO__INVSEQ;
120 				goto done;
121 			}
122 			if (nh->nlmsg_flags & NLM_F_MULTI)
123 				multipart = true;
124 			switch (nh->nlmsg_type) {
125 			case NLMSG_ERROR:
126 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
127 				if (!err->error)
128 					continue;
129 				ret = err->error;
130 				libbpf_nla_dump_errormsg(nh);
131 				goto done;
132 			case NLMSG_DONE:
133 				return 0;
134 			default:
135 				break;
136 			}
137 			if (_fn) {
138 				ret = _fn(nh, fn, cookie);
139 				switch (ret) {
140 				case NL_CONT:
141 					break;
142 				case NL_NEXT:
143 					goto start;
144 				case NL_DONE:
145 					return 0;
146 				default:
147 					return ret;
148 				}
149 			}
150 		}
151 	}
152 	ret = 0;
153 done:
154 	return ret;
155 }
156 
157 static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
158 				    __dump_nlmsg_t parse_msg,
159 				    libbpf_dump_nlmsg_t parse_attr,
160 				    void *cookie)
161 {
162 	__u32 nl_pid = 0;
163 	int sock, ret;
164 
165 	sock = libbpf_netlink_open(&nl_pid);
166 	if (sock < 0)
167 		return sock;
168 
169 	nh->nlmsg_pid = 0;
170 	nh->nlmsg_seq = time(NULL);
171 
172 	if (send(sock, nh, nh->nlmsg_len, 0) < 0) {
173 		ret = -errno;
174 		goto out;
175 	}
176 
177 	ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq,
178 				  parse_msg, parse_attr, cookie);
179 out:
180 	libbpf_netlink_close(sock);
181 	return ret;
182 }
183 
184 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
185 					 __u32 flags)
186 {
187 	struct nlattr *nla;
188 	int ret;
189 	struct {
190 		struct nlmsghdr  nh;
191 		struct ifinfomsg ifinfo;
192 		char             attrbuf[64];
193 	} req;
194 
195 	memset(&req, 0, sizeof(req));
196 	req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
197 	req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
198 	req.nh.nlmsg_type     = RTM_SETLINK;
199 	req.ifinfo.ifi_family = AF_UNSPEC;
200 	req.ifinfo.ifi_index  = ifindex;
201 
202 	nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP);
203 	if (!nla)
204 		return -EMSGSIZE;
205 	ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd));
206 	if (ret < 0)
207 		return ret;
208 	if (flags) {
209 		ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags,
210 				 sizeof(flags));
211 		if (ret < 0)
212 			return ret;
213 	}
214 	if (flags & XDP_FLAGS_REPLACE) {
215 		ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD,
216 				 &old_fd, sizeof(old_fd));
217 		if (ret < 0)
218 			return ret;
219 	}
220 	nlattr_end_nested(&req.nh, nla);
221 
222 	return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
223 }
224 
225 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
226 			     const struct bpf_xdp_set_link_opts *opts)
227 {
228 	int old_fd = -1, ret;
229 
230 	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
231 		return libbpf_err(-EINVAL);
232 
233 	if (OPTS_HAS(opts, old_fd)) {
234 		old_fd = OPTS_GET(opts, old_fd, -1);
235 		flags |= XDP_FLAGS_REPLACE;
236 	}
237 
238 	ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
239 	return libbpf_err(ret);
240 }
241 
242 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
243 {
244 	int ret;
245 
246 	ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
247 	return libbpf_err(ret);
248 }
249 
250 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
251 			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
252 {
253 	struct nlattr *tb[IFLA_MAX + 1], *attr;
254 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
255 	int len;
256 
257 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
258 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
259 
260 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
261 		return -LIBBPF_ERRNO__NLPARSE;
262 
263 	return dump_link_nlmsg(cookie, ifi, tb);
264 }
265 
266 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
267 {
268 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
269 	struct xdp_id_md *xdp_id = cookie;
270 	struct ifinfomsg *ifinfo = msg;
271 	int ret;
272 
273 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
274 		return 0;
275 
276 	if (!tb[IFLA_XDP])
277 		return 0;
278 
279 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
280 	if (ret)
281 		return ret;
282 
283 	if (!xdp_tb[IFLA_XDP_ATTACHED])
284 		return 0;
285 
286 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
287 		xdp_tb[IFLA_XDP_ATTACHED]);
288 
289 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
290 		return 0;
291 
292 	if (xdp_tb[IFLA_XDP_PROG_ID])
293 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
294 			xdp_tb[IFLA_XDP_PROG_ID]);
295 
296 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
297 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
298 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
299 
300 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
301 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
302 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
303 
304 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
305 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
306 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
307 
308 	return 0;
309 }
310 
311 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
312 			  size_t info_size, __u32 flags)
313 {
314 	struct xdp_id_md xdp_id = {};
315 	__u32 mask;
316 	int ret;
317 	struct {
318 		struct nlmsghdr  nh;
319 		struct ifinfomsg ifm;
320 	} req = {
321 		.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
322 		.nh.nlmsg_type  = RTM_GETLINK,
323 		.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
324 		.ifm.ifi_family = AF_PACKET,
325 	};
326 
327 	if (flags & ~XDP_FLAGS_MASK || !info_size)
328 		return libbpf_err(-EINVAL);
329 
330 	/* Check whether the single {HW,DRV,SKB} mode is set */
331 	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
332 	mask = flags - 1;
333 	if (flags && flags & mask)
334 		return libbpf_err(-EINVAL);
335 
336 	xdp_id.ifindex = ifindex;
337 	xdp_id.flags = flags;
338 
339 	ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg,
340 				       get_xdp_info, &xdp_id);
341 	if (!ret) {
342 		size_t sz = min(info_size, sizeof(xdp_id.info));
343 
344 		memcpy(info, &xdp_id.info, sz);
345 		memset((void *) info + sz, 0, info_size - sz);
346 	}
347 
348 	return libbpf_err(ret);
349 }
350 
351 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
352 {
353 	flags &= XDP_FLAGS_MODES;
354 
355 	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
356 		return info->prog_id;
357 	if (flags & XDP_FLAGS_DRV_MODE)
358 		return info->drv_prog_id;
359 	if (flags & XDP_FLAGS_HW_MODE)
360 		return info->hw_prog_id;
361 	if (flags & XDP_FLAGS_SKB_MODE)
362 		return info->skb_prog_id;
363 
364 	return 0;
365 }
366 
367 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
368 {
369 	struct xdp_link_info info;
370 	int ret;
371 
372 	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
373 	if (!ret)
374 		*prog_id = get_xdp_id(&info, flags);
375 
376 	return libbpf_err(ret);
377 }
378 
379 typedef int (*qdisc_config_t)(struct nlmsghdr *nh, struct tcmsg *t,
380 			      size_t maxsz);
381 
382 static int clsact_config(struct nlmsghdr *nh, struct tcmsg *t, size_t maxsz)
383 {
384 	t->tcm_parent = TC_H_CLSACT;
385 	t->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
386 
387 	return nlattr_add(nh, maxsz, TCA_KIND, "clsact", sizeof("clsact"));
388 }
389 
390 static int attach_point_to_config(struct bpf_tc_hook *hook,
391 				  qdisc_config_t *config)
392 {
393 	switch (OPTS_GET(hook, attach_point, 0)) {
394 	case BPF_TC_INGRESS:
395 	case BPF_TC_EGRESS:
396 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
397 		if (OPTS_GET(hook, parent, 0))
398 			return -EINVAL;
399 		*config = &clsact_config;
400 		return 0;
401 	case BPF_TC_CUSTOM:
402 		return -EOPNOTSUPP;
403 	default:
404 		return -EINVAL;
405 	}
406 }
407 
408 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
409 			     __u32 *parent)
410 {
411 	switch (attach_point) {
412 	case BPF_TC_INGRESS:
413 	case BPF_TC_EGRESS:
414 		if (*parent)
415 			return -EINVAL;
416 		*parent = TC_H_MAKE(TC_H_CLSACT,
417 				    attach_point == BPF_TC_INGRESS ?
418 				    TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
419 		break;
420 	case BPF_TC_CUSTOM:
421 		if (!*parent)
422 			return -EINVAL;
423 		break;
424 	default:
425 		return -EINVAL;
426 	}
427 	return 0;
428 }
429 
430 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
431 {
432 	qdisc_config_t config;
433 	int ret;
434 	struct {
435 		struct nlmsghdr nh;
436 		struct tcmsg tc;
437 		char buf[256];
438 	} req;
439 
440 	ret = attach_point_to_config(hook, &config);
441 	if (ret < 0)
442 		return ret;
443 
444 	memset(&req, 0, sizeof(req));
445 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
446 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
447 	req.nh.nlmsg_type  = cmd;
448 	req.tc.tcm_family  = AF_UNSPEC;
449 	req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
450 
451 	ret = config(&req.nh, &req.tc, sizeof(req));
452 	if (ret < 0)
453 		return ret;
454 
455 	return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
456 }
457 
458 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
459 {
460 	return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
461 }
462 
463 static int tc_qdisc_delete(struct bpf_tc_hook *hook)
464 {
465 	return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
466 }
467 
468 int bpf_tc_hook_create(struct bpf_tc_hook *hook)
469 {
470 	int ret;
471 
472 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
473 	    OPTS_GET(hook, ifindex, 0) <= 0)
474 		return libbpf_err(-EINVAL);
475 
476 	ret = tc_qdisc_create_excl(hook);
477 	return libbpf_err(ret);
478 }
479 
480 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
481 			   const struct bpf_tc_opts *opts,
482 			   const bool flush);
483 
484 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
485 {
486 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
487 	    OPTS_GET(hook, ifindex, 0) <= 0)
488 		return libbpf_err(-EINVAL);
489 
490 	switch (OPTS_GET(hook, attach_point, 0)) {
491 	case BPF_TC_INGRESS:
492 	case BPF_TC_EGRESS:
493 		return libbpf_err(__bpf_tc_detach(hook, NULL, true));
494 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
495 		return libbpf_err(tc_qdisc_delete(hook));
496 	case BPF_TC_CUSTOM:
497 		return libbpf_err(-EOPNOTSUPP);
498 	default:
499 		return libbpf_err(-EINVAL);
500 	}
501 }
502 
503 struct bpf_cb_ctx {
504 	struct bpf_tc_opts *opts;
505 	bool processed;
506 };
507 
508 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
509 			 bool unicast)
510 {
511 	struct nlattr *tbb[TCA_BPF_MAX + 1];
512 	struct bpf_cb_ctx *info = cookie;
513 
514 	if (!info || !info->opts)
515 		return -EINVAL;
516 	if (unicast && info->processed)
517 		return -EINVAL;
518 	if (!tb[TCA_OPTIONS])
519 		return NL_CONT;
520 
521 	libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
522 	if (!tbb[TCA_BPF_ID])
523 		return -EINVAL;
524 
525 	OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
526 	OPTS_SET(info->opts, handle, tc->tcm_handle);
527 	OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
528 
529 	info->processed = true;
530 	return unicast ? NL_NEXT : NL_DONE;
531 }
532 
533 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
534 		       void *cookie)
535 {
536 	struct tcmsg *tc = NLMSG_DATA(nh);
537 	struct nlattr *tb[TCA_MAX + 1];
538 
539 	libbpf_nla_parse(tb, TCA_MAX,
540 			 (struct nlattr *)((char *)tc + NLMSG_ALIGN(sizeof(*tc))),
541 			 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
542 	if (!tb[TCA_KIND])
543 		return NL_CONT;
544 	return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
545 }
546 
547 static int tc_add_fd_and_name(struct nlmsghdr *nh, size_t maxsz, int fd)
548 {
549 	struct bpf_prog_info info = {};
550 	__u32 info_len = sizeof(info);
551 	char name[256];
552 	int len, ret;
553 
554 	ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
555 	if (ret < 0)
556 		return ret;
557 
558 	ret = nlattr_add(nh, maxsz, TCA_BPF_FD, &fd, sizeof(fd));
559 	if (ret < 0)
560 		return ret;
561 	len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
562 	if (len < 0)
563 		return -errno;
564 	if (len >= sizeof(name))
565 		return -ENAMETOOLONG;
566 	return nlattr_add(nh, maxsz, TCA_BPF_NAME, name, len + 1);
567 }
568 
569 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
570 {
571 	__u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
572 	int ret, ifindex, attach_point, prog_fd;
573 	struct bpf_cb_ctx info = {};
574 	struct nlattr *nla;
575 	struct {
576 		struct nlmsghdr nh;
577 		struct tcmsg tc;
578 		char buf[256];
579 	} req;
580 
581 	if (!hook || !opts ||
582 	    !OPTS_VALID(hook, bpf_tc_hook) ||
583 	    !OPTS_VALID(opts, bpf_tc_opts))
584 		return libbpf_err(-EINVAL);
585 
586 	ifindex      = OPTS_GET(hook, ifindex, 0);
587 	parent       = OPTS_GET(hook, parent, 0);
588 	attach_point = OPTS_GET(hook, attach_point, 0);
589 
590 	handle       = OPTS_GET(opts, handle, 0);
591 	priority     = OPTS_GET(opts, priority, 0);
592 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
593 	prog_id      = OPTS_GET(opts, prog_id, 0);
594 	flags        = OPTS_GET(opts, flags, 0);
595 
596 	if (ifindex <= 0 || !prog_fd || prog_id)
597 		return libbpf_err(-EINVAL);
598 	if (priority > UINT16_MAX)
599 		return libbpf_err(-EINVAL);
600 	if (flags & ~BPF_TC_F_REPLACE)
601 		return libbpf_err(-EINVAL);
602 
603 	flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
604 	protocol = ETH_P_ALL;
605 
606 	memset(&req, 0, sizeof(req));
607 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
608 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
609 			     NLM_F_ECHO | flags;
610 	req.nh.nlmsg_type  = RTM_NEWTFILTER;
611 	req.tc.tcm_family  = AF_UNSPEC;
612 	req.tc.tcm_ifindex = ifindex;
613 	req.tc.tcm_handle  = handle;
614 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
615 
616 	ret = tc_get_tcm_parent(attach_point, &parent);
617 	if (ret < 0)
618 		return libbpf_err(ret);
619 	req.tc.tcm_parent = parent;
620 
621 	ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
622 	if (ret < 0)
623 		return libbpf_err(ret);
624 	nla = nlattr_begin_nested(&req.nh, sizeof(req), TCA_OPTIONS);
625 	if (!nla)
626 		return libbpf_err(-EMSGSIZE);
627 	ret = tc_add_fd_and_name(&req.nh, sizeof(req), prog_fd);
628 	if (ret < 0)
629 		return libbpf_err(ret);
630 	bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
631 	ret = nlattr_add(&req.nh, sizeof(req), TCA_BPF_FLAGS, &bpf_flags,
632 			 sizeof(bpf_flags));
633 	if (ret < 0)
634 		return libbpf_err(ret);
635 	nlattr_end_nested(&req.nh, nla);
636 
637 	info.opts = opts;
638 
639 	ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
640 	if (ret < 0)
641 		return libbpf_err(ret);
642 	if (!info.processed)
643 		return libbpf_err(-ENOENT);
644 	return ret;
645 }
646 
647 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
648 			   const struct bpf_tc_opts *opts,
649 			   const bool flush)
650 {
651 	__u32 protocol = 0, handle, priority, parent, prog_id, flags;
652 	int ret, ifindex, attach_point, prog_fd;
653 	struct {
654 		struct nlmsghdr nh;
655 		struct tcmsg tc;
656 		char buf[256];
657 	} req;
658 
659 	if (!hook ||
660 	    !OPTS_VALID(hook, bpf_tc_hook) ||
661 	    !OPTS_VALID(opts, bpf_tc_opts))
662 		return -EINVAL;
663 
664 	ifindex      = OPTS_GET(hook, ifindex, 0);
665 	parent       = OPTS_GET(hook, parent, 0);
666 	attach_point = OPTS_GET(hook, attach_point, 0);
667 
668 	handle       = OPTS_GET(opts, handle, 0);
669 	priority     = OPTS_GET(opts, priority, 0);
670 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
671 	prog_id      = OPTS_GET(opts, prog_id, 0);
672 	flags        = OPTS_GET(opts, flags, 0);
673 
674 	if (ifindex <= 0 || flags || prog_fd || prog_id)
675 		return -EINVAL;
676 	if (priority > UINT16_MAX)
677 		return -EINVAL;
678 	if (!flush) {
679 		if (!handle || !priority)
680 			return -EINVAL;
681 		protocol = ETH_P_ALL;
682 	} else {
683 		if (handle || priority)
684 			return -EINVAL;
685 	}
686 
687 	memset(&req, 0, sizeof(req));
688 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
689 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
690 	req.nh.nlmsg_type  = RTM_DELTFILTER;
691 	req.tc.tcm_family  = AF_UNSPEC;
692 	req.tc.tcm_ifindex = ifindex;
693 	if (!flush) {
694 		req.tc.tcm_handle = handle;
695 		req.tc.tcm_info   = TC_H_MAKE(priority << 16, htons(protocol));
696 	}
697 
698 	ret = tc_get_tcm_parent(attach_point, &parent);
699 	if (ret < 0)
700 		return ret;
701 	req.tc.tcm_parent = parent;
702 
703 	if (!flush) {
704 		ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND,
705 				 "bpf", sizeof("bpf"));
706 		if (ret < 0)
707 			return ret;
708 	}
709 
710 	return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
711 }
712 
713 int bpf_tc_detach(const struct bpf_tc_hook *hook,
714 		  const struct bpf_tc_opts *opts)
715 {
716 	int ret;
717 
718 	if (!opts)
719 		return libbpf_err(-EINVAL);
720 
721 	ret = __bpf_tc_detach(hook, opts, false);
722 	return libbpf_err(ret);
723 }
724 
725 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
726 {
727 	__u32 protocol, handle, priority, parent, prog_id, flags;
728 	int ret, ifindex, attach_point, prog_fd;
729 	struct bpf_cb_ctx info = {};
730 	struct {
731 		struct nlmsghdr nh;
732 		struct tcmsg tc;
733 		char buf[256];
734 	} req;
735 
736 	if (!hook || !opts ||
737 	    !OPTS_VALID(hook, bpf_tc_hook) ||
738 	    !OPTS_VALID(opts, bpf_tc_opts))
739 		return libbpf_err(-EINVAL);
740 
741 	ifindex      = OPTS_GET(hook, ifindex, 0);
742 	parent       = OPTS_GET(hook, parent, 0);
743 	attach_point = OPTS_GET(hook, attach_point, 0);
744 
745 	handle       = OPTS_GET(opts, handle, 0);
746 	priority     = OPTS_GET(opts, priority, 0);
747 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
748 	prog_id      = OPTS_GET(opts, prog_id, 0);
749 	flags        = OPTS_GET(opts, flags, 0);
750 
751 	if (ifindex <= 0 || flags || prog_fd || prog_id ||
752 	    !handle || !priority)
753 		return libbpf_err(-EINVAL);
754 	if (priority > UINT16_MAX)
755 		return libbpf_err(-EINVAL);
756 
757 	protocol = ETH_P_ALL;
758 
759 	memset(&req, 0, sizeof(req));
760 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
761 	req.nh.nlmsg_flags = NLM_F_REQUEST;
762 	req.nh.nlmsg_type  = RTM_GETTFILTER;
763 	req.tc.tcm_family  = AF_UNSPEC;
764 	req.tc.tcm_ifindex = ifindex;
765 	req.tc.tcm_handle  = handle;
766 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
767 
768 	ret = tc_get_tcm_parent(attach_point, &parent);
769 	if (ret < 0)
770 		return libbpf_err(ret);
771 	req.tc.tcm_parent = parent;
772 
773 	ret = nlattr_add(&req.nh, sizeof(req), TCA_KIND, "bpf", sizeof("bpf"));
774 	if (ret < 0)
775 		return libbpf_err(ret);
776 
777 	info.opts = opts;
778 
779 	ret = libbpf_netlink_send_recv(&req.nh, get_tc_info, NULL, &info);
780 	if (ret < 0)
781 		return libbpf_err(ret);
782 	if (!info.processed)
783 		return libbpf_err(-ENOENT);
784 	return ret;
785 }
786