xref: /linux/tools/lib/bpf/netlink.c (revision f6f3bac08ff9855d803081a353a1fafaa8845739)
1 // SPDX-License-Identifier: LGPL-2.1
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 int bpf_netlink_open(__u32 *nl_pid)
22 {
23 	struct sockaddr_nl sa;
24 	socklen_t addrlen;
25 	int one = 1, ret;
26 	int sock;
27 
28 	memset(&sa, 0, sizeof(sa));
29 	sa.nl_family = AF_NETLINK;
30 
31 	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
32 	if (sock < 0)
33 		return -errno;
34 
35 	if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
36 		       &one, sizeof(one)) < 0) {
37 		fprintf(stderr, "Netlink error reporting not supported\n");
38 	}
39 
40 	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
41 		ret = -errno;
42 		goto cleanup;
43 	}
44 
45 	addrlen = sizeof(sa);
46 	if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
47 		ret = -errno;
48 		goto cleanup;
49 	}
50 
51 	if (addrlen != sizeof(sa)) {
52 		ret = -LIBBPF_ERRNO__INTERNAL;
53 		goto cleanup;
54 	}
55 
56 	*nl_pid = sa.nl_pid;
57 	return sock;
58 
59 cleanup:
60 	close(sock);
61 	return ret;
62 }
63 
64 static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
65 			    __dump_nlmsg_t _fn, dump_nlmsg_t fn,
66 			    void *cookie)
67 {
68 	struct nlmsgerr *err;
69 	struct nlmsghdr *nh;
70 	char buf[4096];
71 	int len, ret;
72 
73 	while (1) {
74 		len = recv(sock, buf, sizeof(buf), 0);
75 		if (len < 0) {
76 			ret = -errno;
77 			goto done;
78 		}
79 
80 		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
81 		     nh = NLMSG_NEXT(nh, len)) {
82 			if (nh->nlmsg_pid != nl_pid) {
83 				ret = -LIBBPF_ERRNO__WRNGPID;
84 				goto done;
85 			}
86 			if (nh->nlmsg_seq != seq) {
87 				ret = -LIBBPF_ERRNO__INVSEQ;
88 				goto done;
89 			}
90 			switch (nh->nlmsg_type) {
91 			case NLMSG_ERROR:
92 				err = (struct nlmsgerr *)NLMSG_DATA(nh);
93 				if (!err->error)
94 					continue;
95 				ret = err->error;
96 				nla_dump_errormsg(nh);
97 				goto done;
98 			case NLMSG_DONE:
99 				return 0;
100 			default:
101 				break;
102 			}
103 			if (_fn) {
104 				ret = _fn(nh, fn, cookie);
105 				if (ret)
106 					return ret;
107 			}
108 		}
109 	}
110 	ret = 0;
111 done:
112 	return ret;
113 }
114 
115 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
116 {
117 	int sock, seq = 0, ret;
118 	struct nlattr *nla, *nla_xdp;
119 	struct {
120 		struct nlmsghdr  nh;
121 		struct ifinfomsg ifinfo;
122 		char             attrbuf[64];
123 	} req;
124 	__u32 nl_pid;
125 
126 	sock = bpf_netlink_open(&nl_pid);
127 	if (sock < 0)
128 		return sock;
129 
130 	memset(&req, 0, sizeof(req));
131 	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
132 	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
133 	req.nh.nlmsg_type = RTM_SETLINK;
134 	req.nh.nlmsg_pid = 0;
135 	req.nh.nlmsg_seq = ++seq;
136 	req.ifinfo.ifi_family = AF_UNSPEC;
137 	req.ifinfo.ifi_index = ifindex;
138 
139 	/* started nested attribute for XDP */
140 	nla = (struct nlattr *)(((char *)&req)
141 				+ NLMSG_ALIGN(req.nh.nlmsg_len));
142 	nla->nla_type = NLA_F_NESTED | IFLA_XDP;
143 	nla->nla_len = NLA_HDRLEN;
144 
145 	/* add XDP fd */
146 	nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
147 	nla_xdp->nla_type = IFLA_XDP_FD;
148 	nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
149 	memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
150 	nla->nla_len += nla_xdp->nla_len;
151 
152 	/* if user passed in any flags, add those too */
153 	if (flags) {
154 		nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
155 		nla_xdp->nla_type = IFLA_XDP_FLAGS;
156 		nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
157 		memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
158 		nla->nla_len += nla_xdp->nla_len;
159 	}
160 
161 	req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
162 
163 	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
164 		ret = -errno;
165 		goto cleanup;
166 	}
167 	ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
168 
169 cleanup:
170 	close(sock);
171 	return ret;
172 }
173 
174 static int __dump_link_nlmsg(struct nlmsghdr *nlh, dump_nlmsg_t dump_link_nlmsg,
175 			     void *cookie)
176 {
177 	struct nlattr *tb[IFLA_MAX + 1], *attr;
178 	struct ifinfomsg *ifi = NLMSG_DATA(nlh);
179 	int len;
180 
181 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
182 	attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
183 	if (nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
184 		return -LIBBPF_ERRNO__NLPARSE;
185 
186 	return dump_link_nlmsg(cookie, ifi, tb);
187 }
188 
189 int nl_get_link(int sock, unsigned int nl_pid, dump_nlmsg_t dump_link_nlmsg,
190 		void *cookie)
191 {
192 	struct {
193 		struct nlmsghdr nlh;
194 		struct ifinfomsg ifm;
195 	} req = {
196 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
197 		.nlh.nlmsg_type = RTM_GETLINK,
198 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
199 		.ifm.ifi_family = AF_PACKET,
200 	};
201 	int seq = time(NULL);
202 
203 	req.nlh.nlmsg_seq = seq;
204 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
205 		return -errno;
206 
207 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
208 				dump_link_nlmsg, cookie);
209 }
210 
211 static int __dump_class_nlmsg(struct nlmsghdr *nlh,
212 			      dump_nlmsg_t dump_class_nlmsg, void *cookie)
213 {
214 	struct nlattr *tb[TCA_MAX + 1], *attr;
215 	struct tcmsg *t = NLMSG_DATA(nlh);
216 	int len;
217 
218 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
219 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
220 	if (nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
221 		return -LIBBPF_ERRNO__NLPARSE;
222 
223 	return dump_class_nlmsg(cookie, t, tb);
224 }
225 
226 int nl_get_class(int sock, unsigned int nl_pid, int ifindex,
227 		 dump_nlmsg_t dump_class_nlmsg, void *cookie)
228 {
229 	struct {
230 		struct nlmsghdr nlh;
231 		struct tcmsg t;
232 	} req = {
233 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
234 		.nlh.nlmsg_type = RTM_GETTCLASS,
235 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
236 		.t.tcm_family = AF_UNSPEC,
237 		.t.tcm_ifindex = ifindex,
238 	};
239 	int seq = time(NULL);
240 
241 	req.nlh.nlmsg_seq = seq;
242 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
243 		return -errno;
244 
245 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
246 				dump_class_nlmsg, cookie);
247 }
248 
249 static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
250 			      dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
251 {
252 	struct nlattr *tb[TCA_MAX + 1], *attr;
253 	struct tcmsg *t = NLMSG_DATA(nlh);
254 	int len;
255 
256 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
257 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
258 	if (nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
259 		return -LIBBPF_ERRNO__NLPARSE;
260 
261 	return dump_qdisc_nlmsg(cookie, t, tb);
262 }
263 
264 int nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
265 		 dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
266 {
267 	struct {
268 		struct nlmsghdr nlh;
269 		struct tcmsg t;
270 	} req = {
271 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
272 		.nlh.nlmsg_type = RTM_GETQDISC,
273 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
274 		.t.tcm_family = AF_UNSPEC,
275 		.t.tcm_ifindex = ifindex,
276 	};
277 	int seq = time(NULL);
278 
279 	req.nlh.nlmsg_seq = seq;
280 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
281 		return -errno;
282 
283 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
284 				dump_qdisc_nlmsg, cookie);
285 }
286 
287 static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
288 			       dump_nlmsg_t dump_filter_nlmsg, void *cookie)
289 {
290 	struct nlattr *tb[TCA_MAX + 1], *attr;
291 	struct tcmsg *t = NLMSG_DATA(nlh);
292 	int len;
293 
294 	len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
295 	attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
296 	if (nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
297 		return -LIBBPF_ERRNO__NLPARSE;
298 
299 	return dump_filter_nlmsg(cookie, t, tb);
300 }
301 
302 int nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
303 		  dump_nlmsg_t dump_filter_nlmsg, void *cookie)
304 {
305 	struct {
306 		struct nlmsghdr nlh;
307 		struct tcmsg t;
308 	} req = {
309 		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
310 		.nlh.nlmsg_type = RTM_GETTFILTER,
311 		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
312 		.t.tcm_family = AF_UNSPEC,
313 		.t.tcm_ifindex = ifindex,
314 		.t.tcm_parent = handle,
315 	};
316 	int seq = time(NULL);
317 
318 	req.nlh.nlmsg_seq = seq;
319 	if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
320 		return -errno;
321 
322 	return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
323 				dump_filter_nlmsg, cookie);
324 }
325