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