xref: /kernel/linux/linux-5.10/tools/lib/bpf/netlink.c (revision 8c2ecf20)
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 <linux/bpf.h>
8#include <linux/rtnetlink.h>
9#include <sys/socket.h>
10#include <errno.h>
11#include <time.h>
12
13#include "bpf.h"
14#include "libbpf.h"
15#include "libbpf_internal.h"
16#include "nlattr.h"
17
18#ifndef SOL_NETLINK
19#define SOL_NETLINK 270
20#endif
21
22typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
23
24typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
25			      void *cookie);
26
27struct xdp_id_md {
28	int ifindex;
29	__u32 flags;
30	struct xdp_link_info info;
31};
32
33static int libbpf_netlink_open(__u32 *nl_pid)
34{
35	struct sockaddr_nl sa;
36	socklen_t addrlen;
37	int one = 1, ret;
38	int sock;
39
40	memset(&sa, 0, sizeof(sa));
41	sa.nl_family = AF_NETLINK;
42
43	sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
44	if (sock < 0)
45		return -errno;
46
47	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
48		       &one, sizeof(one)) < 0) {
49		pr_warn("Netlink error reporting not supported\n");
50	}
51
52	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
53		ret = -errno;
54		goto cleanup;
55	}
56
57	addrlen = sizeof(sa);
58	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
59		ret = -errno;
60		goto cleanup;
61	}
62
63	if (addrlen != sizeof(sa)) {
64		ret = -LIBBPF_ERRNO__INTERNAL;
65		goto cleanup;
66	}
67
68	*nl_pid = sa.nl_pid;
69	return sock;
70
71cleanup:
72	close(sock);
73	return ret;
74}
75
76static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
77			    __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
78			    void *cookie)
79{
80	bool multipart = true;
81	struct nlmsgerr *err;
82	struct nlmsghdr *nh;
83	char buf[4096];
84	int len, ret;
85
86	while (multipart) {
87		multipart = false;
88		len = recv(sock, buf, sizeof(buf), 0);
89		if (len < 0) {
90			ret = -errno;
91			goto done;
92		}
93
94		if (len == 0)
95			break;
96
97		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
98		     nh = NLMSG_NEXT(nh, len)) {
99			if (nh->nlmsg_pid != nl_pid) {
100				ret = -LIBBPF_ERRNO__WRNGPID;
101				goto done;
102			}
103			if (nh->nlmsg_seq != seq) {
104				ret = -LIBBPF_ERRNO__INVSEQ;
105				goto done;
106			}
107			if (nh->nlmsg_flags & NLM_F_MULTI)
108				multipart = true;
109			switch (nh->nlmsg_type) {
110			case NLMSG_ERROR:
111				err = (struct nlmsgerr *)NLMSG_DATA(nh);
112				if (!err->error)
113					continue;
114				ret = err->error;
115				libbpf_nla_dump_errormsg(nh);
116				goto done;
117			case NLMSG_DONE:
118				return 0;
119			default:
120				break;
121			}
122			if (_fn) {
123				ret = _fn(nh, fn, cookie);
124				if (ret)
125					return ret;
126			}
127		}
128	}
129	ret = 0;
130done:
131	return ret;
132}
133
134static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
135					 __u32 flags)
136{
137	int sock, seq = 0, ret;
138	struct nlattr *nla, *nla_xdp;
139	struct {
140		struct nlmsghdr  nh;
141		struct ifinfomsg ifinfo;
142		char             attrbuf[64];
143	} req;
144	__u32 nl_pid = 0;
145
146	sock = libbpf_netlink_open(&nl_pid);
147	if (sock < 0)
148		return sock;
149
150	memset(&req, 0, sizeof(req));
151	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
152	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
153	req.nh.nlmsg_type = RTM_SETLINK;
154	req.nh.nlmsg_pid = 0;
155	req.nh.nlmsg_seq = ++seq;
156	req.ifinfo.ifi_family = AF_UNSPEC;
157	req.ifinfo.ifi_index = ifindex;
158
159	/* started nested attribute for XDP */
160	nla = (struct nlattr *)(((char *)&req)
161				+ NLMSG_ALIGN(req.nh.nlmsg_len));
162	nla->nla_type = NLA_F_NESTED | IFLA_XDP;
163	nla->nla_len = NLA_HDRLEN;
164
165	/* add XDP fd */
166	nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
167	nla_xdp->nla_type = IFLA_XDP_FD;
168	nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
169	memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
170	nla->nla_len += nla_xdp->nla_len;
171
172	/* if user passed in any flags, add those too */
173	if (flags) {
174		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
175		nla_xdp->nla_type = IFLA_XDP_FLAGS;
176		nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
177		memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
178		nla->nla_len += nla_xdp->nla_len;
179	}
180
181	if (flags & XDP_FLAGS_REPLACE) {
182		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
183		nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD;
184		nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd);
185		memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd));
186		nla->nla_len += nla_xdp->nla_len;
187	}
188
189	req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
190
191	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
192		ret = -errno;
193		goto cleanup;
194	}
195	ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
196
197cleanup:
198	close(sock);
199	return ret;
200}
201
202int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
203			     const struct bpf_xdp_set_link_opts *opts)
204{
205	int old_fd = -1;
206
207	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
208		return -EINVAL;
209
210	if (OPTS_HAS(opts, old_fd)) {
211		old_fd = OPTS_GET(opts, old_fd, -1);
212		flags |= XDP_FLAGS_REPLACE;
213	}
214
215	return __bpf_set_link_xdp_fd_replace(ifindex, fd,
216					     old_fd,
217					     flags);
218}
219
220int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
221{
222	return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
223}
224
225static int __dump_link_nlmsg(struct nlmsghdr *nlh,
226			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
227{
228	struct nlattr *tb[IFLA_MAX + 1], *attr;
229	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
230	int len;
231
232	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
233	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
234	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
235		return -LIBBPF_ERRNO__NLPARSE;
236
237	return dump_link_nlmsg(cookie, ifi, tb);
238}
239
240static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
241{
242	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
243	struct xdp_id_md *xdp_id = cookie;
244	struct ifinfomsg *ifinfo = msg;
245	int ret;
246
247	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
248		return 0;
249
250	if (!tb[IFLA_XDP])
251		return 0;
252
253	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
254	if (ret)
255		return ret;
256
257	if (!xdp_tb[IFLA_XDP_ATTACHED])
258		return 0;
259
260	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
261		xdp_tb[IFLA_XDP_ATTACHED]);
262
263	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
264		return 0;
265
266	if (xdp_tb[IFLA_XDP_PROG_ID])
267		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
268			xdp_tb[IFLA_XDP_PROG_ID]);
269
270	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
271		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
272			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
273
274	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
275		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
276			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
277
278	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
279		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
280			xdp_tb[IFLA_XDP_HW_PROG_ID]);
281
282	return 0;
283}
284
285static int libbpf_nl_get_link(int sock, unsigned int nl_pid,
286			      libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
287
288int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
289			  size_t info_size, __u32 flags)
290{
291	struct xdp_id_md xdp_id = {};
292	int sock, ret;
293	__u32 nl_pid = 0;
294	__u32 mask;
295
296	if (flags & ~XDP_FLAGS_MASK || !info_size)
297		return -EINVAL;
298
299	/* Check whether the single {HW,DRV,SKB} mode is set */
300	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
301	mask = flags - 1;
302	if (flags && flags & mask)
303		return -EINVAL;
304
305	sock = libbpf_netlink_open(&nl_pid);
306	if (sock < 0)
307		return sock;
308
309	xdp_id.ifindex = ifindex;
310	xdp_id.flags = flags;
311
312	ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
313	if (!ret) {
314		size_t sz = min(info_size, sizeof(xdp_id.info));
315
316		memcpy(info, &xdp_id.info, sz);
317		memset((void *) info + sz, 0, info_size - sz);
318	}
319
320	close(sock);
321	return ret;
322}
323
324static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
325{
326	flags &= XDP_FLAGS_MODES;
327
328	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
329		return info->prog_id;
330	if (flags & XDP_FLAGS_DRV_MODE)
331		return info->drv_prog_id;
332	if (flags & XDP_FLAGS_HW_MODE)
333		return info->hw_prog_id;
334	if (flags & XDP_FLAGS_SKB_MODE)
335		return info->skb_prog_id;
336
337	return 0;
338}
339
340int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
341{
342	struct xdp_link_info info;
343	int ret;
344
345	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
346	if (!ret)
347		*prog_id = get_xdp_id(&info, flags);
348
349	return ret;
350}
351
352int libbpf_nl_get_link(int sock, unsigned int nl_pid,
353		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
354{
355	struct {
356		struct nlmsghdr nlh;
357		struct ifinfomsg ifm;
358	} req = {
359		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
360		.nlh.nlmsg_type = RTM_GETLINK,
361		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
362		.ifm.ifi_family = AF_PACKET,
363	};
364	int seq = time(NULL);
365
366	req.nlh.nlmsg_seq = seq;
367	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
368		return -errno;
369
370	return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
371				dump_link_nlmsg, cookie);
372}
373