cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

netlink.c (20348B)


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