diff options
Diffstat (limited to 'tools/lib/bpf/bpf.c')
| -rw-r--r-- | tools/lib/bpf/bpf.c | 135 | 
1 files changed, 135 insertions, 0 deletions
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 5128677e4117..592a58a2b681 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-2.1 +  /*   * common eBPF ELF operations.   * @@ -25,6 +27,16 @@  #include <asm/unistd.h>  #include <linux/bpf.h>  #include "bpf.h" +#include "libbpf.h" +#include "nlattr.h" +#include <linux/rtnetlink.h> +#include <linux/if_link.h> +#include <sys/socket.h> +#include <errno.h> + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif  /*   * When building perf, unistd.h is overridden. __NR_bpf is @@ -46,7 +58,9 @@  # endif  #endif +#ifndef min  #define min(x, y) ((x) < (y) ? (x) : (y)) +#endif  static inline __u64 ptr_to_u64(const void *ptr)  { @@ -413,3 +427,124 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)  	return err;  } + +int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) +{ +	struct sockaddr_nl sa; +	int sock, seq = 0, len, ret = -1; +	char buf[4096]; +	struct nlattr *nla, *nla_xdp; +	struct { +		struct nlmsghdr  nh; +		struct ifinfomsg ifinfo; +		char             attrbuf[64]; +	} req; +	struct nlmsghdr *nh; +	struct nlmsgerr *err; +	socklen_t addrlen; +	int one = 1; + +	memset(&sa, 0, sizeof(sa)); +	sa.nl_family = AF_NETLINK; + +	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); +	if (sock < 0) { +		return -errno; +	} + +	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, +		       &one, sizeof(one)) < 0) { +		fprintf(stderr, "Netlink error reporting not supported\n"); +	} + +	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { +		ret = -errno; +		goto cleanup; +	} + +	addrlen = sizeof(sa); +	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { +		ret = -errno; +		goto cleanup; +	} + +	if (addrlen != sizeof(sa)) { +		ret = -LIBBPF_ERRNO__INTERNAL; +		goto cleanup; +	} + +	memset(&req, 0, sizeof(req)); +	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); +	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; +	req.nh.nlmsg_type = RTM_SETLINK; +	req.nh.nlmsg_pid = 0; +	req.nh.nlmsg_seq = ++seq; +	req.ifinfo.ifi_family = AF_UNSPEC; +	req.ifinfo.ifi_index = ifindex; + +	/* started nested attribute for XDP */ +	nla = (struct nlattr *)(((char *)&req) +				+ NLMSG_ALIGN(req.nh.nlmsg_len)); +	nla->nla_type = NLA_F_NESTED | IFLA_XDP; +	nla->nla_len = NLA_HDRLEN; + +	/* add XDP fd */ +	nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); +	nla_xdp->nla_type = IFLA_XDP_FD; +	nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); +	memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); +	nla->nla_len += nla_xdp->nla_len; + +	/* if user passed in any flags, add those too */ +	if (flags) { +		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); +		nla_xdp->nla_type = IFLA_XDP_FLAGS; +		nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); +		memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); +		nla->nla_len += nla_xdp->nla_len; +	} + +	req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); + +	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { +		ret = -errno; +		goto cleanup; +	} + +	len = recv(sock, buf, sizeof(buf), 0); +	if (len < 0) { +		ret = -errno; +		goto cleanup; +	} + +	for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); +	     nh = NLMSG_NEXT(nh, len)) { +		if (nh->nlmsg_pid != sa.nl_pid) { +			ret = -LIBBPF_ERRNO__WRNGPID; +			goto cleanup; +		} +		if (nh->nlmsg_seq != seq) { +			ret = -LIBBPF_ERRNO__INVSEQ; +			goto cleanup; +		} +		switch (nh->nlmsg_type) { +		case NLMSG_ERROR: +			err = (struct nlmsgerr *)NLMSG_DATA(nh); +			if (!err->error) +				continue; +			ret = err->error; +			nla_dump_errormsg(nh); +			goto cleanup; +		case NLMSG_DONE: +			break; +		default: +			break; +		} +	} + +	ret = 0; + +cleanup: +	close(sock); +	return ret; +}  | 
