xref: /linux-6.15/tools/lib/bpf/netlink.c (revision 7084566a)
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 /* make sure libbpf doesn't use kernel-only integer typedefs */
19 #pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
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, 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 int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
80 			    __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
81 			    void *cookie)
82 {
83 	bool multipart = true;
84 	struct nlmsgerr *err;
85 	struct nlmsghdr *nh;
86 	char buf[4096];
87 	int len, ret;
88 
89 	while (multipart) {
90 		multipart = false;
91 		len = recv(sock, buf, sizeof(buf), 0);
92 		if (len < 0) {
93 			ret = -errno;
94 			goto done;
95 		}
96 
97 		if (len == 0)
98 			break;
99 
100 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
101 		     nh = NLMSG_NEXT(nh, len)) {
102 			if (nh->nlmsg_pid != nl_pid) {
103 				ret = -LIBBPF_ERRNO__WRNGPID;
104 				goto done;
105 			}
106 			if (nh->nlmsg_seq != seq) {
107 				ret = -LIBBPF_ERRNO__INVSEQ;
108 				goto done;
109 			}
110 			if (nh->nlmsg_flags & NLM_F_MULTI)
111 				multipart = true;
112 			switch (nh->nlmsg_type) {
113 			case NLMSG_ERROR:
114 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
115 				if (!err->error)
116 					continue;
117 				ret = err->error;
118 				libbpf_nla_dump_errormsg(nh);
119 				goto done;
120 			case NLMSG_DONE:
121 				return 0;
122 			default:
123 				break;
124 			}
125 			if (_fn) {
126 				ret = _fn(nh, fn, cookie);
127 				if (ret)
128 					return ret;
129 			}
130 		}
131 	}
132 	ret = 0;
133 done:
134 	return ret;
135 }
136 
137 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
138 					 __u32 flags)
139 {
140 	int sock, seq = 0, ret;
141 	struct nlattr *nla, *nla_xdp;
142 	struct {
143 		struct nlmsghdr  nh;
144 		struct ifinfomsg ifinfo;
145 		char             attrbuf[64];
146 	} req;
147 	__u32 nl_pid = 0;
148 
149 	sock = libbpf_netlink_open(&nl_pid);
150 	if (sock < 0)
151 		return sock;
152 
153 	memset(&req, 0, sizeof(req));
154 	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
155 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
156 	req.nh.nlmsg_type = RTM_SETLINK;
157 	req.nh.nlmsg_pid = 0;
158 	req.nh.nlmsg_seq = ++seq;
159 	req.ifinfo.ifi_family = AF_UNSPEC;
160 	req.ifinfo.ifi_index = ifindex;
161 
162 	/* started nested attribute for XDP */
163 	nla = (struct nlattr *)(((char *)&req)
164 				+ NLMSG_ALIGN(req.nh.nlmsg_len));
165 	nla->nla_type = NLA_F_NESTED | IFLA_XDP;
166 	nla->nla_len = NLA_HDRLEN;
167 
168 	/* add XDP fd */
169 	nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
170 	nla_xdp->nla_type = IFLA_XDP_FD;
171 	nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
172 	memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
173 	nla->nla_len += nla_xdp->nla_len;
174 
175 	/* if user passed in any flags, add those too */
176 	if (flags) {
177 		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
178 		nla_xdp->nla_type = IFLA_XDP_FLAGS;
179 		nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
180 		memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
181 		nla->nla_len += nla_xdp->nla_len;
182 	}
183 
184 	if (flags & XDP_FLAGS_REPLACE) {
185 		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
186 		nla_xdp->nla_type = IFLA_XDP_EXPECTED_FD;
187 		nla_xdp->nla_len = NLA_HDRLEN + sizeof(old_fd);
188 		memcpy((char *)nla_xdp + NLA_HDRLEN, &old_fd, sizeof(old_fd));
189 		nla->nla_len += nla_xdp->nla_len;
190 	}
191 
192 	req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
193 
194 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
195 		ret = -errno;
196 		goto cleanup;
197 	}
198 	ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
199 
200 cleanup:
201 	close(sock);
202 	return ret;
203 }
204 
205 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
206 			     const struct bpf_xdp_set_link_opts *opts)
207 {
208 	int old_fd = -1;
209 
210 	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
211 		return -EINVAL;
212 
213 	if (OPTS_HAS(opts, old_fd)) {
214 		old_fd = OPTS_GET(opts, old_fd, -1);
215 		flags |= XDP_FLAGS_REPLACE;
216 	}
217 
218 	return __bpf_set_link_xdp_fd_replace(ifindex, fd,
219 					     old_fd,
220 					     flags);
221 }
222 
223 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
224 {
225 	return __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags);
226 }
227 
228 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
229 			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
230 {
231 	struct nlattr *tb[IFLA_MAX + 1], *attr;
232 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
233 	int len;
234 
235 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
236 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
237 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
238 		return -LIBBPF_ERRNO__NLPARSE;
239 
240 	return dump_link_nlmsg(cookie, ifi, tb);
241 }
242 
243 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
244 {
245 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
246 	struct xdp_id_md *xdp_id = cookie;
247 	struct ifinfomsg *ifinfo = msg;
248 	int ret;
249 
250 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
251 		return 0;
252 
253 	if (!tb[IFLA_XDP])
254 		return 0;
255 
256 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
257 	if (ret)
258 		return ret;
259 
260 	if (!xdp_tb[IFLA_XDP_ATTACHED])
261 		return 0;
262 
263 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
264 		xdp_tb[IFLA_XDP_ATTACHED]);
265 
266 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
267 		return 0;
268 
269 	if (xdp_tb[IFLA_XDP_PROG_ID])
270 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
271 			xdp_tb[IFLA_XDP_PROG_ID]);
272 
273 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
274 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
275 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
276 
277 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
278 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
279 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
280 
281 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
282 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
283 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
284 
285 	return 0;
286 }
287 
288 static int libbpf_nl_get_link(int sock, unsigned int nl_pid,
289 			      libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
290 
291 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
292 			  size_t info_size, __u32 flags)
293 {
294 	struct xdp_id_md xdp_id = {};
295 	int sock, ret;
296 	__u32 nl_pid = 0;
297 	__u32 mask;
298 
299 	if (flags & ~XDP_FLAGS_MASK || !info_size)
300 		return -EINVAL;
301 
302 	/* Check whether the single {HW,DRV,SKB} mode is set */
303 	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
304 	mask = flags - 1;
305 	if (flags && flags & mask)
306 		return -EINVAL;
307 
308 	sock = libbpf_netlink_open(&nl_pid);
309 	if (sock < 0)
310 		return sock;
311 
312 	xdp_id.ifindex = ifindex;
313 	xdp_id.flags = flags;
314 
315 	ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
316 	if (!ret) {
317 		size_t sz = min(info_size, sizeof(xdp_id.info));
318 
319 		memcpy(info, &xdp_id.info, sz);
320 		memset((void *) info + sz, 0, info_size - sz);
321 	}
322 
323 	close(sock);
324 	return ret;
325 }
326 
327 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
328 {
329 	flags &= XDP_FLAGS_MODES;
330 
331 	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
332 		return info->prog_id;
333 	if (flags & XDP_FLAGS_DRV_MODE)
334 		return info->drv_prog_id;
335 	if (flags & XDP_FLAGS_HW_MODE)
336 		return info->hw_prog_id;
337 	if (flags & XDP_FLAGS_SKB_MODE)
338 		return info->skb_prog_id;
339 
340 	return 0;
341 }
342 
343 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
344 {
345 	struct xdp_link_info info;
346 	int ret;
347 
348 	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
349 	if (!ret)
350 		*prog_id = get_xdp_id(&info, flags);
351 
352 	return ret;
353 }
354 
355 int libbpf_nl_get_link(int sock, unsigned int nl_pid,
356 		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
357 {
358 	struct {
359 		struct nlmsghdr nlh;
360 		struct ifinfomsg ifm;
361 	} req = {
362 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
363 		.nlh.nlmsg_type = RTM_GETLINK,
364 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
365 		.ifm.ifi_family = AF_PACKET,
366 	};
367 	int seq = time(NULL);
368 
369 	req.nlh.nlmsg_seq = seq;
370 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
371 		return -errno;
372 
373 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
374 				dump_link_nlmsg, cookie);
375 }
376