diff options
Diffstat (limited to 'tools/lib/bpf/netlink.c')
| -rw-r--r-- | tools/lib/bpf/netlink.c | 180 | 
1 files changed, 140 insertions, 40 deletions
diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c index 39f25e09b51e..cbc8967d5402 100644 --- a/tools/lib/bpf/netlink.c +++ b/tools/lib/bpf/netlink.c @@ -87,29 +87,75 @@ enum {  	NL_DONE,  }; +static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags) +{ +	int len; + +	do { +		len = recvmsg(sock, mhdr, flags); +	} while (len < 0 && (errno == EINTR || errno == EAGAIN)); + +	if (len < 0) +		return -errno; +	return len; +} + +static int alloc_iov(struct iovec *iov, int len) +{ +	void *nbuf; + +	nbuf = realloc(iov->iov_base, len); +	if (!nbuf) +		return -ENOMEM; + +	iov->iov_base = nbuf; +	iov->iov_len = len; +	return 0; +} +  static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,  			       __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,  			       void *cookie)  { +	struct iovec iov = {}; +	struct msghdr mhdr = { +		.msg_iov = &iov, +		.msg_iovlen = 1, +	};  	bool multipart = true;  	struct nlmsgerr *err;  	struct nlmsghdr *nh; -	char buf[4096];  	int len, ret; +	ret = alloc_iov(&iov, 4096); +	if (ret) +		goto done; +  	while (multipart) {  start:  		multipart = false; -		len = recv(sock, buf, sizeof(buf), 0); +		len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);  		if (len < 0) { -			ret = -errno; +			ret = len; +			goto done; +		} + +		if (len > iov.iov_len) { +			ret = alloc_iov(&iov, len); +			if (ret) +				goto done; +		} + +		len = netlink_recvmsg(sock, &mhdr, 0); +		if (len < 0) { +			ret = len;  			goto done;  		}  		if (len == 0)  			break; -		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); +		for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);  		     nh = NLMSG_NEXT(nh, len)) {  			if (nh->nlmsg_pid != nl_pid) {  				ret = -LIBBPF_ERRNO__WRNGPID; @@ -130,7 +176,8 @@ start:  				libbpf_nla_dump_errormsg(nh);  				goto done;  			case NLMSG_DONE: -				return 0; +				ret = 0; +				goto done;  			default:  				break;  			} @@ -142,15 +189,17 @@ start:  				case NL_NEXT:  					goto start;  				case NL_DONE: -					return 0; +					ret = 0; +					goto done;  				default: -					return ret; +					goto done;  				}  			}  		}  	}  	ret = 0;  done: +	free(iov.iov_base);  	return ret;  } @@ -217,6 +266,28 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,  	return libbpf_netlink_send_recv(&req, NULL, NULL, NULL);  } +int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts) +{ +	int old_prog_fd, err; + +	if (!OPTS_VALID(opts, bpf_xdp_attach_opts)) +		return libbpf_err(-EINVAL); + +	old_prog_fd = OPTS_GET(opts, old_prog_fd, 0); +	if (old_prog_fd) +		flags |= XDP_FLAGS_REPLACE; +	else +		old_prog_fd = -1; + +	err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags); +	return libbpf_err(err); +} + +int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts) +{ +	return bpf_xdp_attach(ifindex, -1, flags, opts); +} +  int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,  			     const struct bpf_xdp_set_link_opts *opts)  { @@ -303,69 +374,98 @@ static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)  	return 0;  } -int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, -			  size_t info_size, __u32 flags) +int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)  { -	struct xdp_id_md xdp_id = {}; -	__u32 mask; -	int ret;  	struct libbpf_nla_req req = {  		.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),  		.nh.nlmsg_type     = RTM_GETLINK,  		.nh.nlmsg_flags    = NLM_F_DUMP | NLM_F_REQUEST,  		.ifinfo.ifi_family = AF_PACKET,  	}; +	struct xdp_id_md xdp_id = {}; +	int err; -	if (flags & ~XDP_FLAGS_MASK || !info_size) +	if (!OPTS_VALID(opts, bpf_xdp_query_opts)) +		return libbpf_err(-EINVAL); + +	if (xdp_flags & ~XDP_FLAGS_MASK)  		return libbpf_err(-EINVAL);  	/* Check whether the single {HW,DRV,SKB} mode is set */ -	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE); -	mask = flags - 1; -	if (flags && flags & mask) +	xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE; +	if (xdp_flags & (xdp_flags - 1))  		return libbpf_err(-EINVAL);  	xdp_id.ifindex = ifindex; -	xdp_id.flags = flags; +	xdp_id.flags = xdp_flags; -	ret = libbpf_netlink_send_recv(&req, __dump_link_nlmsg, +	err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg,  				       get_xdp_info, &xdp_id); -	if (!ret) { -		size_t sz = min(info_size, sizeof(xdp_id.info)); +	if (err) +		return libbpf_err(err); -		memcpy(info, &xdp_id.info, sz); -		memset((void *) info + sz, 0, info_size - sz); -	} +	OPTS_SET(opts, prog_id, xdp_id.info.prog_id); +	OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id); +	OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id); +	OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id); +	OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode); -	return libbpf_err(ret); +	return 0;  } -static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags) +int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, +			  size_t info_size, __u32 flags)  { -	flags &= XDP_FLAGS_MODES; +	LIBBPF_OPTS(bpf_xdp_query_opts, opts); +	size_t sz; +	int err; -	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags) -		return info->prog_id; -	if (flags & XDP_FLAGS_DRV_MODE) -		return info->drv_prog_id; -	if (flags & XDP_FLAGS_HW_MODE) -		return info->hw_prog_id; -	if (flags & XDP_FLAGS_SKB_MODE) -		return info->skb_prog_id; +	if (!info_size) +		return libbpf_err(-EINVAL); + +	err = bpf_xdp_query(ifindex, flags, &opts); +	if (err) +		return libbpf_err(err); + +	/* struct xdp_link_info field layout matches struct bpf_xdp_query_opts +	 * layout after sz field +	 */ +	sz = min(info_size, offsetofend(struct xdp_link_info, attach_mode)); +	memcpy(info, &opts.prog_id, sz); +	memset((void *)info + sz, 0, info_size - sz);  	return 0;  } -int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) +int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)  { -	struct xdp_link_info info; +	LIBBPF_OPTS(bpf_xdp_query_opts, opts);  	int ret; -	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags); -	if (!ret) -		*prog_id = get_xdp_id(&info, flags); +	ret = bpf_xdp_query(ifindex, flags, &opts); +	if (ret) +		return libbpf_err(ret); -	return libbpf_err(ret); +	flags &= XDP_FLAGS_MODES; + +	if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags) +		*prog_id = opts.prog_id; +	else if (flags & XDP_FLAGS_DRV_MODE) +		*prog_id = opts.drv_prog_id; +	else if (flags & XDP_FLAGS_HW_MODE) +		*prog_id = opts.hw_prog_id; +	else if (flags & XDP_FLAGS_SKB_MODE) +		*prog_id = opts.skb_prog_id; +	else +		*prog_id = 0; + +	return 0; +} + + +int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) +{ +	return bpf_xdp_query_id(ifindex, flags, prog_id);  }  typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);  | 
