xref: /linux/tools/lib/bpf/netlink.c (revision 0a9f4a20c6153d187c8ee58133357ac671372f5f)
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 netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
91 {
92 	int len;
93 
94 	do {
95 		len = recvmsg(sock, mhdr, flags);
96 	} while (len < 0 && (errno == EINTR || errno == EAGAIN));
97 
98 	if (len < 0)
99 		return -errno;
100 	return len;
101 }
102 
103 static int alloc_iov(struct iovec *iov, int len)
104 {
105 	void *nbuf;
106 
107 	nbuf = realloc(iov->iov_base, len);
108 	if (!nbuf)
109 		return -ENOMEM;
110 
111 	iov->iov_base = nbuf;
112 	iov->iov_len = len;
113 	return 0;
114 }
115 
116 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
117 			       __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
118 			       void *cookie)
119 {
120 	struct iovec iov = {};
121 	struct msghdr mhdr = {
122 		.msg_iov = &iov,
123 		.msg_iovlen = 1,
124 	};
125 	bool multipart = true;
126 	struct nlmsgerr *err;
127 	struct nlmsghdr *nh;
128 	int len, ret;
129 
130 	ret = alloc_iov(&iov, 4096);
131 	if (ret)
132 		goto done;
133 
134 	while (multipart) {
135 start:
136 		multipart = false;
137 		len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
138 		if (len < 0) {
139 			ret = len;
140 			goto done;
141 		}
142 
143 		if (len > iov.iov_len) {
144 			ret = alloc_iov(&iov, len);
145 			if (ret)
146 				goto done;
147 		}
148 
149 		len = netlink_recvmsg(sock, &mhdr, 0);
150 		if (len < 0) {
151 			ret = len;
152 			goto done;
153 		}
154 
155 		if (len == 0)
156 			break;
157 
158 		for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
159 		     nh = NLMSG_NEXT(nh, len)) {
160 			if (nh->nlmsg_pid != nl_pid) {
161 				ret = -LIBBPF_ERRNO__WRNGPID;
162 				goto done;
163 			}
164 			if (nh->nlmsg_seq != seq) {
165 				ret = -LIBBPF_ERRNO__INVSEQ;
166 				goto done;
167 			}
168 			if (nh->nlmsg_flags & NLM_F_MULTI)
169 				multipart = true;
170 			switch (nh->nlmsg_type) {
171 			case NLMSG_ERROR:
172 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
173 				if (!err->error)
174 					continue;
175 				ret = err->error;
176 				libbpf_nla_dump_errormsg(nh);
177 				goto done;
178 			case NLMSG_DONE:
179 				return 0;
180 			default:
181 				break;
182 			}
183 			if (_fn) {
184 				ret = _fn(nh, fn, cookie);
185 				switch (ret) {
186 				case NL_CONT:
187 					break;
188 				case NL_NEXT:
189 					goto start;
190 				case NL_DONE:
191 					return 0;
192 				default:
193 					return ret;
194 				}
195 			}
196 		}
197 	}
198 	ret = 0;
199 done:
200 	free(iov.iov_base);
201 	return ret;
202 }
203 
204 static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
205 				    __dump_nlmsg_t parse_msg,
206 				    libbpf_dump_nlmsg_t parse_attr,
207 				    void *cookie)
208 {
209 	__u32 nl_pid = 0;
210 	int sock, ret;
211 
212 	sock = libbpf_netlink_open(&nl_pid);
213 	if (sock < 0)
214 		return sock;
215 
216 	req->nh.nlmsg_pid = 0;
217 	req->nh.nlmsg_seq = time(NULL);
218 
219 	if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
220 		ret = -errno;
221 		goto out;
222 	}
223 
224 	ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
225 				  parse_msg, parse_attr, cookie);
226 out:
227 	libbpf_netlink_close(sock);
228 	return ret;
229 }
230 
231 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
232 					 __u32 flags)
233 {
234 	struct nlattr *nla;
235 	int ret;
236 	struct libbpf_nla_req req;
237 
238 	memset(&req, 0, sizeof(req));
239 	req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
240 	req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
241 	req.nh.nlmsg_type     = RTM_SETLINK;
242 	req.ifinfo.ifi_family = AF_UNSPEC;
243 	req.ifinfo.ifi_index  = ifindex;
244 
245 	nla = nlattr_begin_nested(&req, IFLA_XDP);
246 	if (!nla)
247 		return -EMSGSIZE;
248 	ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
249 	if (ret < 0)
250 		return ret;
251 	if (flags) {
252 		ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
253 		if (ret < 0)
254 			return ret;
255 	}
256 	if (flags & XDP_FLAGS_REPLACE) {
257 		ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
258 				 sizeof(old_fd));
259 		if (ret < 0)
260 			return ret;
261 	}
262 	nlattr_end_nested(&req, nla);
263 
264 	return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
265 }
266 
267 int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
268 {
269 	int old_prog_fd, err;
270 
271 	if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
272 		return libbpf_err(-EINVAL);
273 
274 	old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
275 	if (old_prog_fd)
276 		flags |= XDP_FLAGS_REPLACE;
277 	else
278 		old_prog_fd = -1;
279 
280 	err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
281 	return libbpf_err(err);
282 }
283 
284 int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
285 {
286 	return bpf_xdp_attach(ifindex, -1, flags, opts);
287 }
288 
289 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
290 			     const struct bpf_xdp_set_link_opts *opts)
291 {
292 	int old_fd = -1, ret;
293 
294 	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
295 		return libbpf_err(-EINVAL);
296 
297 	if (OPTS_HAS(opts, old_fd)) {
298 		old_fd = OPTS_GET(opts, old_fd, -1);
299 		flags |= XDP_FLAGS_REPLACE;
300 	}
301 
302 	ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags);
303 	return libbpf_err(ret);
304 }
305 
306 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
307 {
308 	int ret;
309 
310 	ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
311 	return libbpf_err(ret);
312 }
313 
314 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
315 			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
316 {
317 	struct nlattr *tb[IFLA_MAX + 1], *attr;
318 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
319 	int len;
320 
321 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
322 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
323 
324 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
325 		return -LIBBPF_ERRNO__NLPARSE;
326 
327 	return dump_link_nlmsg(cookie, ifi, tb);
328 }
329 
330 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
331 {
332 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
333 	struct xdp_id_md *xdp_id = cookie;
334 	struct ifinfomsg *ifinfo = msg;
335 	int ret;
336 
337 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
338 		return 0;
339 
340 	if (!tb[IFLA_XDP])
341 		return 0;
342 
343 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
344 	if (ret)
345 		return ret;
346 
347 	if (!xdp_tb[IFLA_XDP_ATTACHED])
348 		return 0;
349 
350 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
351 		xdp_tb[IFLA_XDP_ATTACHED]);
352 
353 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
354 		return 0;
355 
356 	if (xdp_tb[IFLA_XDP_PROG_ID])
357 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
358 			xdp_tb[IFLA_XDP_PROG_ID]);
359 
360 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
361 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
362 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
363 
364 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
365 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
366 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
367 
368 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
369 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
370 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
371 
372 	return 0;
373 }
374 
375 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
376 {
377 	struct libbpf_nla_req req = {
378 		.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
379 		.nh.nlmsg_type     = RTM_GETLINK,
380 		.nh.nlmsg_flags    = NLM_F_DUMP | NLM_F_REQUEST,
381 		.ifinfo.ifi_family = AF_PACKET,
382 	};
383 	struct xdp_id_md xdp_id = {};
384 	int err;
385 
386 	if (!OPTS_VALID(opts, bpf_xdp_query_opts))
387 		return libbpf_err(-EINVAL);
388 
389 	if (xdp_flags & ~XDP_FLAGS_MASK)
390 		return libbpf_err(-EINVAL);
391 
392 	/* Check whether the single {HW,DRV,SKB} mode is set */
393 	xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
394 	if (xdp_flags & (xdp_flags - 1))
395 		return libbpf_err(-EINVAL);
396 
397 	xdp_id.ifindex = ifindex;
398 	xdp_id.flags = xdp_flags;
399 
400 	err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,
401 				       get_xdp_info, &xdp_id);
402 	if (err)
403 		return libbpf_err(err);
404 
405 	OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
406 	OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
407 	OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
408 	OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
409 	OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
410 
411 	return 0;
412 }
413 
414 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
415 			  size_t info_size, __u32 flags)
416 {
417 	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
418 	size_t sz;
419 	int err;
420 
421 	if (!info_size)
422 		return libbpf_err(-EINVAL);
423 
424 	err = bpf_xdp_query(ifindex, flags, &opts);
425 	if (err)
426 		return libbpf_err(err);
427 
428 	/* struct xdp_link_info field layout matches struct bpf_xdp_query_opts
429 	 * layout after sz field
430 	 */
431 	sz = min(info_size, offsetofend(struct xdp_link_info, attach_mode));
432 	memcpy(info, &opts.prog_id, sz);
433 	memset((void *)info + sz, 0, info_size - sz);
434 
435 	return 0;
436 }
437 
438 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
439 {
440 	LIBBPF_OPTS(bpf_xdp_query_opts, opts);
441 	int ret;
442 
443 	ret = bpf_xdp_query(ifindex, flags, &opts);
444 	if (ret)
445 		return libbpf_err(ret);
446 
447 	flags &= XDP_FLAGS_MODES;
448 
449 	if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
450 		*prog_id = opts.prog_id;
451 	else if (flags & XDP_FLAGS_DRV_MODE)
452 		*prog_id = opts.drv_prog_id;
453 	else if (flags & XDP_FLAGS_HW_MODE)
454 		*prog_id = opts.hw_prog_id;
455 	else if (flags & XDP_FLAGS_SKB_MODE)
456 		*prog_id = opts.skb_prog_id;
457 	else
458 		*prog_id = 0;
459 
460 	return 0;
461 }
462 
463 
464 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
465 {
466 	return bpf_xdp_query_id(ifindex, flags, prog_id);
467 }
468 
469 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
470 
471 static int clsact_config(struct libbpf_nla_req *req)
472 {
473 	req->tc.tcm_parent = TC_H_CLSACT;
474 	req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
475 
476 	return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
477 }
478 
479 static int attach_point_to_config(struct bpf_tc_hook *hook,
480 				  qdisc_config_t *config)
481 {
482 	switch (OPTS_GET(hook, attach_point, 0)) {
483 	case BPF_TC_INGRESS:
484 	case BPF_TC_EGRESS:
485 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
486 		if (OPTS_GET(hook, parent, 0))
487 			return -EINVAL;
488 		*config = &clsact_config;
489 		return 0;
490 	case BPF_TC_CUSTOM:
491 		return -EOPNOTSUPP;
492 	default:
493 		return -EINVAL;
494 	}
495 }
496 
497 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
498 			     __u32 *parent)
499 {
500 	switch (attach_point) {
501 	case BPF_TC_INGRESS:
502 	case BPF_TC_EGRESS:
503 		if (*parent)
504 			return -EINVAL;
505 		*parent = TC_H_MAKE(TC_H_CLSACT,
506 				    attach_point == BPF_TC_INGRESS ?
507 				    TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
508 		break;
509 	case BPF_TC_CUSTOM:
510 		if (!*parent)
511 			return -EINVAL;
512 		break;
513 	default:
514 		return -EINVAL;
515 	}
516 	return 0;
517 }
518 
519 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
520 {
521 	qdisc_config_t config;
522 	int ret;
523 	struct libbpf_nla_req req;
524 
525 	ret = attach_point_to_config(hook, &config);
526 	if (ret < 0)
527 		return ret;
528 
529 	memset(&req, 0, sizeof(req));
530 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
531 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
532 	req.nh.nlmsg_type  = cmd;
533 	req.tc.tcm_family  = AF_UNSPEC;
534 	req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
535 
536 	ret = config(&req);
537 	if (ret < 0)
538 		return ret;
539 
540 	return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
541 }
542 
543 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
544 {
545 	return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
546 }
547 
548 static int tc_qdisc_delete(struct bpf_tc_hook *hook)
549 {
550 	return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
551 }
552 
553 int bpf_tc_hook_create(struct bpf_tc_hook *hook)
554 {
555 	int ret;
556 
557 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
558 	    OPTS_GET(hook, ifindex, 0) <= 0)
559 		return libbpf_err(-EINVAL);
560 
561 	ret = tc_qdisc_create_excl(hook);
562 	return libbpf_err(ret);
563 }
564 
565 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
566 			   const struct bpf_tc_opts *opts,
567 			   const bool flush);
568 
569 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
570 {
571 	if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
572 	    OPTS_GET(hook, ifindex, 0) <= 0)
573 		return libbpf_err(-EINVAL);
574 
575 	switch (OPTS_GET(hook, attach_point, 0)) {
576 	case BPF_TC_INGRESS:
577 	case BPF_TC_EGRESS:
578 		return libbpf_err(__bpf_tc_detach(hook, NULL, true));
579 	case BPF_TC_INGRESS | BPF_TC_EGRESS:
580 		return libbpf_err(tc_qdisc_delete(hook));
581 	case BPF_TC_CUSTOM:
582 		return libbpf_err(-EOPNOTSUPP);
583 	default:
584 		return libbpf_err(-EINVAL);
585 	}
586 }
587 
588 struct bpf_cb_ctx {
589 	struct bpf_tc_opts *opts;
590 	bool processed;
591 };
592 
593 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
594 			 bool unicast)
595 {
596 	struct nlattr *tbb[TCA_BPF_MAX + 1];
597 	struct bpf_cb_ctx *info = cookie;
598 
599 	if (!info || !info->opts)
600 		return -EINVAL;
601 	if (unicast && info->processed)
602 		return -EINVAL;
603 	if (!tb[TCA_OPTIONS])
604 		return NL_CONT;
605 
606 	libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
607 	if (!tbb[TCA_BPF_ID])
608 		return -EINVAL;
609 
610 	OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
611 	OPTS_SET(info->opts, handle, tc->tcm_handle);
612 	OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
613 
614 	info->processed = true;
615 	return unicast ? NL_NEXT : NL_DONE;
616 }
617 
618 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
619 		       void *cookie)
620 {
621 	struct tcmsg *tc = NLMSG_DATA(nh);
622 	struct nlattr *tb[TCA_MAX + 1];
623 
624 	libbpf_nla_parse(tb, TCA_MAX,
625 			 (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
626 			 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
627 	if (!tb[TCA_KIND])
628 		return NL_CONT;
629 	return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
630 }
631 
632 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
633 {
634 	struct bpf_prog_info info = {};
635 	__u32 info_len = sizeof(info);
636 	char name[256];
637 	int len, ret;
638 
639 	ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
640 	if (ret < 0)
641 		return ret;
642 
643 	ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
644 	if (ret < 0)
645 		return ret;
646 	len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
647 	if (len < 0)
648 		return -errno;
649 	if (len >= sizeof(name))
650 		return -ENAMETOOLONG;
651 	return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
652 }
653 
654 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
655 {
656 	__u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
657 	int ret, ifindex, attach_point, prog_fd;
658 	struct bpf_cb_ctx info = {};
659 	struct libbpf_nla_req req;
660 	struct nlattr *nla;
661 
662 	if (!hook || !opts ||
663 	    !OPTS_VALID(hook, bpf_tc_hook) ||
664 	    !OPTS_VALID(opts, bpf_tc_opts))
665 		return libbpf_err(-EINVAL);
666 
667 	ifindex      = OPTS_GET(hook, ifindex, 0);
668 	parent       = OPTS_GET(hook, parent, 0);
669 	attach_point = OPTS_GET(hook, attach_point, 0);
670 
671 	handle       = OPTS_GET(opts, handle, 0);
672 	priority     = OPTS_GET(opts, priority, 0);
673 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
674 	prog_id      = OPTS_GET(opts, prog_id, 0);
675 	flags        = OPTS_GET(opts, flags, 0);
676 
677 	if (ifindex <= 0 || !prog_fd || prog_id)
678 		return libbpf_err(-EINVAL);
679 	if (priority > UINT16_MAX)
680 		return libbpf_err(-EINVAL);
681 	if (flags & ~BPF_TC_F_REPLACE)
682 		return libbpf_err(-EINVAL);
683 
684 	flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
685 	protocol = ETH_P_ALL;
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 | NLM_F_CREATE |
690 			     NLM_F_ECHO | flags;
691 	req.nh.nlmsg_type  = RTM_NEWTFILTER;
692 	req.tc.tcm_family  = AF_UNSPEC;
693 	req.tc.tcm_ifindex = ifindex;
694 	req.tc.tcm_handle  = handle;
695 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
696 
697 	ret = tc_get_tcm_parent(attach_point, &parent);
698 	if (ret < 0)
699 		return libbpf_err(ret);
700 	req.tc.tcm_parent = parent;
701 
702 	ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
703 	if (ret < 0)
704 		return libbpf_err(ret);
705 	nla = nlattr_begin_nested(&req, TCA_OPTIONS);
706 	if (!nla)
707 		return libbpf_err(-EMSGSIZE);
708 	ret = tc_add_fd_and_name(&req, prog_fd);
709 	if (ret < 0)
710 		return libbpf_err(ret);
711 	bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
712 	ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
713 	if (ret < 0)
714 		return libbpf_err(ret);
715 	nlattr_end_nested(&req, nla);
716 
717 	info.opts = opts;
718 
719 	ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
720 	if (ret < 0)
721 		return libbpf_err(ret);
722 	if (!info.processed)
723 		return libbpf_err(-ENOENT);
724 	return ret;
725 }
726 
727 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
728 			   const struct bpf_tc_opts *opts,
729 			   const bool flush)
730 {
731 	__u32 protocol = 0, handle, priority, parent, prog_id, flags;
732 	int ret, ifindex, attach_point, prog_fd;
733 	struct libbpf_nla_req req;
734 
735 	if (!hook ||
736 	    !OPTS_VALID(hook, bpf_tc_hook) ||
737 	    !OPTS_VALID(opts, bpf_tc_opts))
738 		return -EINVAL;
739 
740 	ifindex      = OPTS_GET(hook, ifindex, 0);
741 	parent       = OPTS_GET(hook, parent, 0);
742 	attach_point = OPTS_GET(hook, attach_point, 0);
743 
744 	handle       = OPTS_GET(opts, handle, 0);
745 	priority     = OPTS_GET(opts, priority, 0);
746 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
747 	prog_id      = OPTS_GET(opts, prog_id, 0);
748 	flags        = OPTS_GET(opts, flags, 0);
749 
750 	if (ifindex <= 0 || flags || prog_fd || prog_id)
751 		return -EINVAL;
752 	if (priority > UINT16_MAX)
753 		return -EINVAL;
754 	if (!flush) {
755 		if (!handle || !priority)
756 			return -EINVAL;
757 		protocol = ETH_P_ALL;
758 	} else {
759 		if (handle || priority)
760 			return -EINVAL;
761 	}
762 
763 	memset(&req, 0, sizeof(req));
764 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
765 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
766 	req.nh.nlmsg_type  = RTM_DELTFILTER;
767 	req.tc.tcm_family  = AF_UNSPEC;
768 	req.tc.tcm_ifindex = ifindex;
769 	if (!flush) {
770 		req.tc.tcm_handle = handle;
771 		req.tc.tcm_info   = TC_H_MAKE(priority << 16, htons(protocol));
772 	}
773 
774 	ret = tc_get_tcm_parent(attach_point, &parent);
775 	if (ret < 0)
776 		return ret;
777 	req.tc.tcm_parent = parent;
778 
779 	if (!flush) {
780 		ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
781 		if (ret < 0)
782 			return ret;
783 	}
784 
785 	return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);
786 }
787 
788 int bpf_tc_detach(const struct bpf_tc_hook *hook,
789 		  const struct bpf_tc_opts *opts)
790 {
791 	int ret;
792 
793 	if (!opts)
794 		return libbpf_err(-EINVAL);
795 
796 	ret = __bpf_tc_detach(hook, opts, false);
797 	return libbpf_err(ret);
798 }
799 
800 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
801 {
802 	__u32 protocol, handle, priority, parent, prog_id, flags;
803 	int ret, ifindex, attach_point, prog_fd;
804 	struct bpf_cb_ctx info = {};
805 	struct libbpf_nla_req req;
806 
807 	if (!hook || !opts ||
808 	    !OPTS_VALID(hook, bpf_tc_hook) ||
809 	    !OPTS_VALID(opts, bpf_tc_opts))
810 		return libbpf_err(-EINVAL);
811 
812 	ifindex      = OPTS_GET(hook, ifindex, 0);
813 	parent       = OPTS_GET(hook, parent, 0);
814 	attach_point = OPTS_GET(hook, attach_point, 0);
815 
816 	handle       = OPTS_GET(opts, handle, 0);
817 	priority     = OPTS_GET(opts, priority, 0);
818 	prog_fd      = OPTS_GET(opts, prog_fd, 0);
819 	prog_id      = OPTS_GET(opts, prog_id, 0);
820 	flags        = OPTS_GET(opts, flags, 0);
821 
822 	if (ifindex <= 0 || flags || prog_fd || prog_id ||
823 	    !handle || !priority)
824 		return libbpf_err(-EINVAL);
825 	if (priority > UINT16_MAX)
826 		return libbpf_err(-EINVAL);
827 
828 	protocol = ETH_P_ALL;
829 
830 	memset(&req, 0, sizeof(req));
831 	req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
832 	req.nh.nlmsg_flags = NLM_F_REQUEST;
833 	req.nh.nlmsg_type  = RTM_GETTFILTER;
834 	req.tc.tcm_family  = AF_UNSPEC;
835 	req.tc.tcm_ifindex = ifindex;
836 	req.tc.tcm_handle  = handle;
837 	req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
838 
839 	ret = tc_get_tcm_parent(attach_point, &parent);
840 	if (ret < 0)
841 		return libbpf_err(ret);
842 	req.tc.tcm_parent = parent;
843 
844 	ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
845 	if (ret < 0)
846 		return libbpf_err(ret);
847 
848 	info.opts = opts;
849 
850 	ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info);
851 	if (ret < 0)
852 		return libbpf_err(ret);
853 	if (!info.processed)
854 		return libbpf_err(-ENOENT);
855 	return ret;
856 }
857