xref: /linux-6.15/tools/lib/bpf/netlink.c (revision 8bbb77b7)
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 
22 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
23 
24 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
25 			      void *cookie);
26 
27 struct xdp_id_md {
28 	int ifindex;
29 	__u32 flags;
30 	struct xdp_link_info info;
31 };
32 
33 static 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 
71 cleanup:
72 	close(sock);
73 	return ret;
74 }
75 
76 static void libbpf_netlink_close(int sock)
77 {
78 	close(sock);
79 }
80 
81 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
82 			       __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
83 			       void *cookie)
84 {
85 	bool multipart = true;
86 	struct nlmsgerr *err;
87 	struct nlmsghdr *nh;
88 	char buf[4096];
89 	int len, ret;
90 
91 	while (multipart) {
92 		multipart = false;
93 		len = recv(sock, buf, sizeof(buf), 0);
94 		if (len < 0) {
95 			ret = -errno;
96 			goto done;
97 		}
98 
99 		if (len == 0)
100 			break;
101 
102 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
103 		     nh = NLMSG_NEXT(nh, len)) {
104 			if (nh->nlmsg_pid != nl_pid) {
105 				ret = -LIBBPF_ERRNO__WRNGPID;
106 				goto done;
107 			}
108 			if (nh->nlmsg_seq != seq) {
109 				ret = -LIBBPF_ERRNO__INVSEQ;
110 				goto done;
111 			}
112 			if (nh->nlmsg_flags & NLM_F_MULTI)
113 				multipart = true;
114 			switch (nh->nlmsg_type) {
115 			case NLMSG_ERROR:
116 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
117 				if (!err->error)
118 					continue;
119 				ret = err->error;
120 				libbpf_nla_dump_errormsg(nh);
121 				goto done;
122 			case NLMSG_DONE:
123 				return 0;
124 			default:
125 				break;
126 			}
127 			if (_fn) {
128 				ret = _fn(nh, fn, cookie);
129 				if (ret)
130 					return ret;
131 			}
132 		}
133 	}
134 	ret = 0;
135 done:
136 	return ret;
137 }
138 
139 static int libbpf_netlink_send_recv(struct nlmsghdr *nh,
140 				    __dump_nlmsg_t parse_msg,
141 				    libbpf_dump_nlmsg_t parse_attr,
142 				    void *cookie)
143 {
144 	__u32 nl_pid = 0;
145 	int sock, ret;
146 
147 	sock = libbpf_netlink_open(&nl_pid);
148 	if (sock < 0)
149 		return sock;
150 
151 	nh->nlmsg_pid = 0;
152 	nh->nlmsg_seq = time(NULL);
153 
154 	if (send(sock, nh, nh->nlmsg_len, 0) < 0) {
155 		ret = -errno;
156 		goto out;
157 	}
158 
159 	ret = libbpf_netlink_recv(sock, nl_pid, nh->nlmsg_seq,
160 				  parse_msg, parse_attr, cookie);
161 out:
162 	libbpf_netlink_close(sock);
163 	return ret;
164 }
165 
166 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
167 					 __u32 flags)
168 {
169 	struct nlattr *nla;
170 	int ret;
171 	struct {
172 		struct nlmsghdr  nh;
173 		struct ifinfomsg ifinfo;
174 		char             attrbuf[64];
175 	} req;
176 
177 	memset(&req, 0, sizeof(req));
178 	req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
179 	req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
180 	req.nh.nlmsg_type     = RTM_SETLINK;
181 	req.ifinfo.ifi_family = AF_UNSPEC;
182 	req.ifinfo.ifi_index  = ifindex;
183 
184 	nla = nlattr_begin_nested(&req.nh, sizeof(req), IFLA_XDP);
185 	if (!nla)
186 		return -EMSGSIZE;
187 	ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FD, &fd, sizeof(fd));
188 	if (ret < 0)
189 		return ret;
190 	if (flags) {
191 		ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_FLAGS, &flags,
192 				 sizeof(flags));
193 		if (ret < 0)
194 			return ret;
195 	}
196 	if (flags & XDP_FLAGS_REPLACE) {
197 		ret = nlattr_add(&req.nh, sizeof(req), IFLA_XDP_EXPECTED_FD,
198 				 &old_fd, sizeof(old_fd));
199 		if (ret < 0)
200 			return ret;
201 	}
202 	nlattr_end_nested(&req.nh, nla);
203 
204 	return libbpf_netlink_send_recv(&req.nh, NULL, NULL, NULL);
205 }
206 
207 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
208 			     const struct bpf_xdp_set_link_opts *opts)
209 {
210 	int old_fd = -1;
211 
212 	if (!OPTS_VALID(opts, bpf_xdp_set_link_opts))
213 		return -EINVAL;
214 
215 	if (OPTS_HAS(opts, old_fd)) {
216 		old_fd = OPTS_GET(opts, old_fd, -1);
217 		flags |= XDP_FLAGS_REPLACE;
218 	}
219 
220 	return __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, 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 
238 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
239 		return -LIBBPF_ERRNO__NLPARSE;
240 
241 	return dump_link_nlmsg(cookie, ifi, tb);
242 }
243 
244 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
245 {
246 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
247 	struct xdp_id_md *xdp_id = cookie;
248 	struct ifinfomsg *ifinfo = msg;
249 	int ret;
250 
251 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
252 		return 0;
253 
254 	if (!tb[IFLA_XDP])
255 		return 0;
256 
257 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
258 	if (ret)
259 		return ret;
260 
261 	if (!xdp_tb[IFLA_XDP_ATTACHED])
262 		return 0;
263 
264 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
265 		xdp_tb[IFLA_XDP_ATTACHED]);
266 
267 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
268 		return 0;
269 
270 	if (xdp_tb[IFLA_XDP_PROG_ID])
271 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
272 			xdp_tb[IFLA_XDP_PROG_ID]);
273 
274 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
275 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
276 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
277 
278 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
279 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
280 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
281 
282 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
283 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
284 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
285 
286 	return 0;
287 }
288 
289 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
290 			  size_t info_size, __u32 flags)
291 {
292 	struct xdp_id_md xdp_id = {};
293 	__u32 mask;
294 	int ret;
295 	struct {
296 		struct nlmsghdr  nh;
297 		struct ifinfomsg ifm;
298 	} req = {
299 		.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
300 		.nh.nlmsg_type  = RTM_GETLINK,
301 		.nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
302 		.ifm.ifi_family = AF_PACKET,
303 	};
304 
305 	if (flags & ~XDP_FLAGS_MASK || !info_size)
306 		return -EINVAL;
307 
308 	/* Check whether the single {HW,DRV,SKB} mode is set */
309 	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
310 	mask = flags - 1;
311 	if (flags && flags & mask)
312 		return -EINVAL;
313 
314 	xdp_id.ifindex = ifindex;
315 	xdp_id.flags = flags;
316 
317 	ret = libbpf_netlink_send_recv(&req.nh, __dump_link_nlmsg,
318 				       get_xdp_info, &xdp_id);
319 	if (!ret) {
320 		size_t sz = min(info_size, sizeof(xdp_id.info));
321 
322 		memcpy(info, &xdp_id.info, sz);
323 		memset((void *) info + sz, 0, info_size - sz);
324 	}
325 
326 	return ret;
327 }
328 
329 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
330 {
331 	flags &= XDP_FLAGS_MODES;
332 
333 	if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
334 		return info->prog_id;
335 	if (flags & XDP_FLAGS_DRV_MODE)
336 		return info->drv_prog_id;
337 	if (flags & XDP_FLAGS_HW_MODE)
338 		return info->hw_prog_id;
339 	if (flags & XDP_FLAGS_SKB_MODE)
340 		return info->skb_prog_id;
341 
342 	return 0;
343 }
344 
345 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
346 {
347 	struct xdp_link_info info;
348 	int ret;
349 
350 	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
351 	if (!ret)
352 		*prog_id = get_xdp_id(&info, flags);
353 
354 	return ret;
355 }
356