xref: /openbmc/linux/tools/lib/bpf/netlink.c (revision 7b73a9c8)
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 (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
23 			      void *cookie);
24 
25 struct xdp_id_md {
26 	int ifindex;
27 	__u32 flags;
28 	struct xdp_link_info info;
29 };
30 
31 int libbpf_netlink_open(__u32 *nl_pid)
32 {
33 	struct sockaddr_nl sa;
34 	socklen_t addrlen;
35 	int one = 1, ret;
36 	int sock;
37 
38 	memset(&sa, 0, sizeof(sa));
39 	sa.nl_family = AF_NETLINK;
40 
41 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
42 	if (sock < 0)
43 		return -errno;
44 
45 	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
46 		       &one, sizeof(one)) < 0) {
47 		pr_warn("Netlink error reporting not supported\n");
48 	}
49 
50 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
51 		ret = -errno;
52 		goto cleanup;
53 	}
54 
55 	addrlen = sizeof(sa);
56 	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
57 		ret = -errno;
58 		goto cleanup;
59 	}
60 
61 	if (addrlen != sizeof(sa)) {
62 		ret = -LIBBPF_ERRNO__INTERNAL;
63 		goto cleanup;
64 	}
65 
66 	*nl_pid = sa.nl_pid;
67 	return sock;
68 
69 cleanup:
70 	close(sock);
71 	return ret;
72 }
73 
74 static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
75 			    __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
76 			    void *cookie)
77 {
78 	bool multipart = true;
79 	struct nlmsgerr *err;
80 	struct nlmsghdr *nh;
81 	char buf[4096];
82 	int len, ret;
83 
84 	while (multipart) {
85 		multipart = false;
86 		len = recv(sock, buf, sizeof(buf), 0);
87 		if (len < 0) {
88 			ret = -errno;
89 			goto done;
90 		}
91 
92 		if (len == 0)
93 			break;
94 
95 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
96 		     nh = NLMSG_NEXT(nh, len)) {
97 			if (nh->nlmsg_pid != nl_pid) {
98 				ret = -LIBBPF_ERRNO__WRNGPID;
99 				goto done;
100 			}
101 			if (nh->nlmsg_seq != seq) {
102 				ret = -LIBBPF_ERRNO__INVSEQ;
103 				goto done;
104 			}
105 			if (nh->nlmsg_flags & NLM_F_MULTI)
106 				multipart = true;
107 			switch (nh->nlmsg_type) {
108 			case NLMSG_ERROR:
109 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
110 				if (!err->error)
111 					continue;
112 				ret = err->error;
113 				libbpf_nla_dump_errormsg(nh);
114 				goto done;
115 			case NLMSG_DONE:
116 				return 0;
117 			default:
118 				break;
119 			}
120 			if (_fn) {
121 				ret = _fn(nh, fn, cookie);
122 				if (ret)
123 					return ret;
124 			}
125 		}
126 	}
127 	ret = 0;
128 done:
129 	return ret;
130 }
131 
132 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
133 {
134 	int sock, seq = 0, ret;
135 	struct nlattr *nla, *nla_xdp;
136 	struct {
137 		struct nlmsghdr  nh;
138 		struct ifinfomsg ifinfo;
139 		char             attrbuf[64];
140 	} req;
141 	__u32 nl_pid;
142 
143 	sock = libbpf_netlink_open(&nl_pid);
144 	if (sock < 0)
145 		return sock;
146 
147 	memset(&req, 0, sizeof(req));
148 	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
149 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
150 	req.nh.nlmsg_type = RTM_SETLINK;
151 	req.nh.nlmsg_pid = 0;
152 	req.nh.nlmsg_seq = ++seq;
153 	req.ifinfo.ifi_family = AF_UNSPEC;
154 	req.ifinfo.ifi_index = ifindex;
155 
156 	/* started nested attribute for XDP */
157 	nla = (struct nlattr *)(((char *)&req)
158 				+ NLMSG_ALIGN(req.nh.nlmsg_len));
159 	nla->nla_type = NLA_F_NESTED | IFLA_XDP;
160 	nla->nla_len = NLA_HDRLEN;
161 
162 	/* add XDP fd */
163 	nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
164 	nla_xdp->nla_type = IFLA_XDP_FD;
165 	nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
166 	memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
167 	nla->nla_len += nla_xdp->nla_len;
168 
169 	/* if user passed in any flags, add those too */
170 	if (flags) {
171 		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
172 		nla_xdp->nla_type = IFLA_XDP_FLAGS;
173 		nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
174 		memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
175 		nla->nla_len += nla_xdp->nla_len;
176 	}
177 
178 	req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
179 
180 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
181 		ret = -errno;
182 		goto cleanup;
183 	}
184 	ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
185 
186 cleanup:
187 	close(sock);
188 	return ret;
189 }
190 
191 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
192 			     libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
193 {
194 	struct nlattr *tb[IFLA_MAX + 1], *attr;
195 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
196 	int len;
197 
198 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
199 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
200 	if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
201 		return -LIBBPF_ERRNO__NLPARSE;
202 
203 	return dump_link_nlmsg(cookie, ifi, tb);
204 }
205 
206 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
207 {
208 	struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
209 	struct xdp_id_md *xdp_id = cookie;
210 	struct ifinfomsg *ifinfo = msg;
211 	int ret;
212 
213 	if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
214 		return 0;
215 
216 	if (!tb[IFLA_XDP])
217 		return 0;
218 
219 	ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
220 	if (ret)
221 		return ret;
222 
223 	if (!xdp_tb[IFLA_XDP_ATTACHED])
224 		return 0;
225 
226 	xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
227 		xdp_tb[IFLA_XDP_ATTACHED]);
228 
229 	if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
230 		return 0;
231 
232 	if (xdp_tb[IFLA_XDP_PROG_ID])
233 		xdp_id->info.prog_id = libbpf_nla_getattr_u32(
234 			xdp_tb[IFLA_XDP_PROG_ID]);
235 
236 	if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
237 		xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
238 			xdp_tb[IFLA_XDP_SKB_PROG_ID]);
239 
240 	if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
241 		xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
242 			xdp_tb[IFLA_XDP_DRV_PROG_ID]);
243 
244 	if (xdp_tb[IFLA_XDP_HW_PROG_ID])
245 		xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
246 			xdp_tb[IFLA_XDP_HW_PROG_ID]);
247 
248 	return 0;
249 }
250 
251 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
252 			  size_t info_size, __u32 flags)
253 {
254 	struct xdp_id_md xdp_id = {};
255 	int sock, ret;
256 	__u32 nl_pid;
257 	__u32 mask;
258 
259 	if (flags & ~XDP_FLAGS_MASK || !info_size)
260 		return -EINVAL;
261 
262 	/* Check whether the single {HW,DRV,SKB} mode is set */
263 	flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
264 	mask = flags - 1;
265 	if (flags && flags & mask)
266 		return -EINVAL;
267 
268 	sock = libbpf_netlink_open(&nl_pid);
269 	if (sock < 0)
270 		return sock;
271 
272 	xdp_id.ifindex = ifindex;
273 	xdp_id.flags = flags;
274 
275 	ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
276 	if (!ret) {
277 		size_t sz = min(info_size, sizeof(xdp_id.info));
278 
279 		memcpy(info, &xdp_id.info, sz);
280 		memset((void *) info + sz, 0, info_size - sz);
281 	}
282 
283 	close(sock);
284 	return ret;
285 }
286 
287 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
288 {
289 	if (info->attach_mode != XDP_ATTACHED_MULTI)
290 		return info->prog_id;
291 	if (flags & XDP_FLAGS_DRV_MODE)
292 		return info->drv_prog_id;
293 	if (flags & XDP_FLAGS_HW_MODE)
294 		return info->hw_prog_id;
295 	if (flags & XDP_FLAGS_SKB_MODE)
296 		return info->skb_prog_id;
297 
298 	return 0;
299 }
300 
301 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
302 {
303 	struct xdp_link_info info;
304 	int ret;
305 
306 	ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
307 	if (!ret)
308 		*prog_id = get_xdp_id(&info, flags);
309 
310 	return ret;
311 }
312 
313 int libbpf_nl_get_link(int sock, unsigned int nl_pid,
314 		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
315 {
316 	struct {
317 		struct nlmsghdr nlh;
318 		struct ifinfomsg ifm;
319 	} req = {
320 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
321 		.nlh.nlmsg_type = RTM_GETLINK,
322 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
323 		.ifm.ifi_family = AF_PACKET,
324 	};
325 	int seq = time(NULL);
326 
327 	req.nlh.nlmsg_seq = seq;
328 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
329 		return -errno;
330 
331 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
332 				dump_link_nlmsg, cookie);
333 }
334 
335 static int __dump_class_nlmsg(struct nlmsghdr *nlh,
336 			      libbpf_dump_nlmsg_t dump_class_nlmsg,
337 			      void *cookie)
338 {
339 	struct nlattr *tb[TCA_MAX + 1], *attr;
340 	struct tcmsg *t = NLMSG_DATA(nlh);
341 	int len;
342 
343 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
344 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
345 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
346 		return -LIBBPF_ERRNO__NLPARSE;
347 
348 	return dump_class_nlmsg(cookie, t, tb);
349 }
350 
351 int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
352 			libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie)
353 {
354 	struct {
355 		struct nlmsghdr nlh;
356 		struct tcmsg t;
357 	} req = {
358 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
359 		.nlh.nlmsg_type = RTM_GETTCLASS,
360 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
361 		.t.tcm_family = AF_UNSPEC,
362 		.t.tcm_ifindex = ifindex,
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_class_nlmsg,
371 				dump_class_nlmsg, cookie);
372 }
373 
374 static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
375 			      libbpf_dump_nlmsg_t dump_qdisc_nlmsg,
376 			      void *cookie)
377 {
378 	struct nlattr *tb[TCA_MAX + 1], *attr;
379 	struct tcmsg *t = NLMSG_DATA(nlh);
380 	int len;
381 
382 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
383 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
384 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
385 		return -LIBBPF_ERRNO__NLPARSE;
386 
387 	return dump_qdisc_nlmsg(cookie, t, tb);
388 }
389 
390 int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
391 			libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
392 {
393 	struct {
394 		struct nlmsghdr nlh;
395 		struct tcmsg t;
396 	} req = {
397 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
398 		.nlh.nlmsg_type = RTM_GETQDISC,
399 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
400 		.t.tcm_family = AF_UNSPEC,
401 		.t.tcm_ifindex = ifindex,
402 	};
403 	int seq = time(NULL);
404 
405 	req.nlh.nlmsg_seq = seq;
406 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
407 		return -errno;
408 
409 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
410 				dump_qdisc_nlmsg, cookie);
411 }
412 
413 static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
414 			       libbpf_dump_nlmsg_t dump_filter_nlmsg,
415 			       void *cookie)
416 {
417 	struct nlattr *tb[TCA_MAX + 1], *attr;
418 	struct tcmsg *t = NLMSG_DATA(nlh);
419 	int len;
420 
421 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
422 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
423 	if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
424 		return -LIBBPF_ERRNO__NLPARSE;
425 
426 	return dump_filter_nlmsg(cookie, t, tb);
427 }
428 
429 int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
430 			 libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie)
431 {
432 	struct {
433 		struct nlmsghdr nlh;
434 		struct tcmsg t;
435 	} req = {
436 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
437 		.nlh.nlmsg_type = RTM_GETTFILTER,
438 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
439 		.t.tcm_family = AF_UNSPEC,
440 		.t.tcm_ifindex = ifindex,
441 		.t.tcm_parent = handle,
442 	};
443 	int seq = time(NULL);
444 
445 	req.nlh.nlmsg_seq = seq;
446 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
447 		return -errno;
448 
449 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
450 				dump_filter_nlmsg, cookie);
451 }
452