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
libbpf_netlink_open(__u32 * nl_pid)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
bpf_netlink_recv(int sock,__u32 nl_pid,int seq,__dump_nlmsg_t _fn,libbpf_dump_nlmsg_t fn,void * cookie)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
bpf_set_link_xdp_fd(int ifindex,int fd,__u32 flags)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
__dump_link_nlmsg(struct nlmsghdr * nlh,libbpf_dump_nlmsg_t dump_link_nlmsg,void * cookie)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
get_xdp_info(void * cookie,void * msg,struct nlattr ** tb)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
bpf_get_link_xdp_info(int ifindex,struct xdp_link_info * info,size_t info_size,__u32 flags)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
get_xdp_id(struct xdp_link_info * info,__u32 flags)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
bpf_get_link_xdp_id(int ifindex,__u32 * prog_id,__u32 flags)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
libbpf_nl_get_link(int sock,unsigned int nl_pid,libbpf_dump_nlmsg_t dump_link_nlmsg,void * cookie)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
__dump_class_nlmsg(struct nlmsghdr * nlh,libbpf_dump_nlmsg_t dump_class_nlmsg,void * cookie)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
libbpf_nl_get_class(int sock,unsigned int nl_pid,int ifindex,libbpf_dump_nlmsg_t dump_class_nlmsg,void * cookie)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
__dump_qdisc_nlmsg(struct nlmsghdr * nlh,libbpf_dump_nlmsg_t dump_qdisc_nlmsg,void * cookie)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
libbpf_nl_get_qdisc(int sock,unsigned int nl_pid,int ifindex,libbpf_dump_nlmsg_t dump_qdisc_nlmsg,void * cookie)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
__dump_filter_nlmsg(struct nlmsghdr * nlh,libbpf_dump_nlmsg_t dump_filter_nlmsg,void * cookie)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
libbpf_nl_get_filter(int sock,unsigned int nl_pid,int ifindex,int handle,libbpf_dump_nlmsg_t dump_filter_nlmsg,void * cookie)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