libbpf: Fix bpf_xdp_query() in old kernels
[platform/kernel/linux-rpi.git] / tools / lib / bpf / netlink.c
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 <arpa/inet.h>
8 #include <linux/bpf.h>
9 #include <linux/if_ether.h>
10 #include <linux/pkt_cls.h>
11 #include <linux/rtnetlink.h>
12 #include <linux/netdev.h>
13 #include <sys/socket.h>
14 #include <errno.h>
15 #include <time.h>
16
17 #include "bpf.h"
18 #include "libbpf.h"
19 #include "libbpf_internal.h"
20 #include "nlattr.h"
21
22 #ifndef SOL_NETLINK
23 #define SOL_NETLINK 270
24 #endif
25
26 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
27
28 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
29                               void *cookie);
30
31 struct xdp_link_info {
32         __u32 prog_id;
33         __u32 drv_prog_id;
34         __u32 hw_prog_id;
35         __u32 skb_prog_id;
36         __u8 attach_mode;
37 };
38
39 struct xdp_id_md {
40         int ifindex;
41         __u32 flags;
42         struct xdp_link_info info;
43         __u64 feature_flags;
44 };
45
46 struct xdp_features_md {
47         int ifindex;
48         __u64 flags;
49 };
50
51 static int libbpf_netlink_open(__u32 *nl_pid, int proto)
52 {
53         struct sockaddr_nl sa;
54         socklen_t addrlen;
55         int one = 1, ret;
56         int sock;
57
58         memset(&sa, 0, sizeof(sa));
59         sa.nl_family = AF_NETLINK;
60
61         sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto);
62         if (sock < 0)
63                 return -errno;
64
65         if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
66                        &one, sizeof(one)) < 0) {
67                 pr_warn("Netlink error reporting not supported\n");
68         }
69
70         if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
71                 ret = -errno;
72                 goto cleanup;
73         }
74
75         addrlen = sizeof(sa);
76         if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
77                 ret = -errno;
78                 goto cleanup;
79         }
80
81         if (addrlen != sizeof(sa)) {
82                 ret = -LIBBPF_ERRNO__INTERNAL;
83                 goto cleanup;
84         }
85
86         *nl_pid = sa.nl_pid;
87         return sock;
88
89 cleanup:
90         close(sock);
91         return ret;
92 }
93
94 static void libbpf_netlink_close(int sock)
95 {
96         close(sock);
97 }
98
99 enum {
100         NL_CONT,
101         NL_NEXT,
102         NL_DONE,
103 };
104
105 static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags)
106 {
107         int len;
108
109         do {
110                 len = recvmsg(sock, mhdr, flags);
111         } while (len < 0 && (errno == EINTR || errno == EAGAIN));
112
113         if (len < 0)
114                 return -errno;
115         return len;
116 }
117
118 static int alloc_iov(struct iovec *iov, int len)
119 {
120         void *nbuf;
121
122         nbuf = realloc(iov->iov_base, len);
123         if (!nbuf)
124                 return -ENOMEM;
125
126         iov->iov_base = nbuf;
127         iov->iov_len = len;
128         return 0;
129 }
130
131 static int libbpf_netlink_recv(int sock, __u32 nl_pid, int seq,
132                                __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
133                                void *cookie)
134 {
135         struct iovec iov = {};
136         struct msghdr mhdr = {
137                 .msg_iov = &iov,
138                 .msg_iovlen = 1,
139         };
140         bool multipart = true;
141         struct nlmsgerr *err;
142         struct nlmsghdr *nh;
143         int len, ret;
144
145         ret = alloc_iov(&iov, 4096);
146         if (ret)
147                 goto done;
148
149         while (multipart) {
150 start:
151                 multipart = false;
152                 len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC);
153                 if (len < 0) {
154                         ret = len;
155                         goto done;
156                 }
157
158                 if (len > iov.iov_len) {
159                         ret = alloc_iov(&iov, len);
160                         if (ret)
161                                 goto done;
162                 }
163
164                 len = netlink_recvmsg(sock, &mhdr, 0);
165                 if (len < 0) {
166                         ret = len;
167                         goto done;
168                 }
169
170                 if (len == 0)
171                         break;
172
173                 for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len);
174                      nh = NLMSG_NEXT(nh, len)) {
175                         if (nh->nlmsg_pid != nl_pid) {
176                                 ret = -LIBBPF_ERRNO__WRNGPID;
177                                 goto done;
178                         }
179                         if (nh->nlmsg_seq != seq) {
180                                 ret = -LIBBPF_ERRNO__INVSEQ;
181                                 goto done;
182                         }
183                         if (nh->nlmsg_flags & NLM_F_MULTI)
184                                 multipart = true;
185                         switch (nh->nlmsg_type) {
186                         case NLMSG_ERROR:
187                                 err = (struct nlmsgerr *)NLMSG_DATA(nh);
188                                 if (!err->error)
189                                         continue;
190                                 ret = err->error;
191                                 libbpf_nla_dump_errormsg(nh);
192                                 goto done;
193                         case NLMSG_DONE:
194                                 ret = 0;
195                                 goto done;
196                         default:
197                                 break;
198                         }
199                         if (_fn) {
200                                 ret = _fn(nh, fn, cookie);
201                                 switch (ret) {
202                                 case NL_CONT:
203                                         break;
204                                 case NL_NEXT:
205                                         goto start;
206                                 case NL_DONE:
207                                         ret = 0;
208                                         goto done;
209                                 default:
210                                         goto done;
211                                 }
212                         }
213                 }
214         }
215         ret = 0;
216 done:
217         free(iov.iov_base);
218         return ret;
219 }
220
221 static int libbpf_netlink_send_recv(struct libbpf_nla_req *req,
222                                     int proto, __dump_nlmsg_t parse_msg,
223                                     libbpf_dump_nlmsg_t parse_attr,
224                                     void *cookie)
225 {
226         __u32 nl_pid = 0;
227         int sock, ret;
228
229         sock = libbpf_netlink_open(&nl_pid, proto);
230         if (sock < 0)
231                 return sock;
232
233         req->nh.nlmsg_pid = 0;
234         req->nh.nlmsg_seq = time(NULL);
235
236         if (send(sock, req, req->nh.nlmsg_len, 0) < 0) {
237                 ret = -errno;
238                 goto out;
239         }
240
241         ret = libbpf_netlink_recv(sock, nl_pid, req->nh.nlmsg_seq,
242                                   parse_msg, parse_attr, cookie);
243 out:
244         libbpf_netlink_close(sock);
245         return ret;
246 }
247
248 static int parse_genl_family_id(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
249                                 void *cookie)
250 {
251         struct genlmsghdr *gnl = NLMSG_DATA(nh);
252         struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
253         struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
254         __u16 *id = cookie;
255
256         libbpf_nla_parse(tb, CTRL_ATTR_FAMILY_ID, na,
257                          NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
258         if (!tb[CTRL_ATTR_FAMILY_ID])
259                 return NL_CONT;
260
261         *id = libbpf_nla_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
262         return NL_DONE;
263 }
264
265 static int libbpf_netlink_resolve_genl_family_id(const char *name,
266                                                  __u16 len, __u16 *id)
267 {
268         struct libbpf_nla_req req = {
269                 .nh.nlmsg_len   = NLMSG_LENGTH(GENL_HDRLEN),
270                 .nh.nlmsg_type  = GENL_ID_CTRL,
271                 .nh.nlmsg_flags = NLM_F_REQUEST,
272                 .gnl.cmd        = CTRL_CMD_GETFAMILY,
273                 .gnl.version    = 2,
274         };
275         int err;
276
277         err = nlattr_add(&req, CTRL_ATTR_FAMILY_NAME, name, len);
278         if (err < 0)
279                 return err;
280
281         return libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
282                                         parse_genl_family_id, NULL, id);
283 }
284
285 static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
286                                          __u32 flags)
287 {
288         struct nlattr *nla;
289         int ret;
290         struct libbpf_nla_req req;
291
292         memset(&req, 0, sizeof(req));
293         req.nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg));
294         req.nh.nlmsg_flags    = NLM_F_REQUEST | NLM_F_ACK;
295         req.nh.nlmsg_type     = RTM_SETLINK;
296         req.ifinfo.ifi_family = AF_UNSPEC;
297         req.ifinfo.ifi_index  = ifindex;
298
299         nla = nlattr_begin_nested(&req, IFLA_XDP);
300         if (!nla)
301                 return -EMSGSIZE;
302         ret = nlattr_add(&req, IFLA_XDP_FD, &fd, sizeof(fd));
303         if (ret < 0)
304                 return ret;
305         if (flags) {
306                 ret = nlattr_add(&req, IFLA_XDP_FLAGS, &flags, sizeof(flags));
307                 if (ret < 0)
308                         return ret;
309         }
310         if (flags & XDP_FLAGS_REPLACE) {
311                 ret = nlattr_add(&req, IFLA_XDP_EXPECTED_FD, &old_fd,
312                                  sizeof(old_fd));
313                 if (ret < 0)
314                         return ret;
315         }
316         nlattr_end_nested(&req, nla);
317
318         return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
319 }
320
321 int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts)
322 {
323         int old_prog_fd, err;
324
325         if (!OPTS_VALID(opts, bpf_xdp_attach_opts))
326                 return libbpf_err(-EINVAL);
327
328         old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
329         if (old_prog_fd)
330                 flags |= XDP_FLAGS_REPLACE;
331         else
332                 old_prog_fd = -1;
333
334         err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags);
335         return libbpf_err(err);
336 }
337
338 int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts)
339 {
340         return bpf_xdp_attach(ifindex, -1, flags, opts);
341 }
342
343 static int __dump_link_nlmsg(struct nlmsghdr *nlh,
344                              libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
345 {
346         struct nlattr *tb[IFLA_MAX + 1], *attr;
347         struct ifinfomsg *ifi = NLMSG_DATA(nlh);
348         int len;
349
350         len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
351         attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
352
353         if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
354                 return -LIBBPF_ERRNO__NLPARSE;
355
356         return dump_link_nlmsg(cookie, ifi, tb);
357 }
358
359 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
360 {
361         struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
362         struct xdp_id_md *xdp_id = cookie;
363         struct ifinfomsg *ifinfo = msg;
364         int ret;
365
366         if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
367                 return 0;
368
369         if (!tb[IFLA_XDP])
370                 return 0;
371
372         ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
373         if (ret)
374                 return ret;
375
376         if (!xdp_tb[IFLA_XDP_ATTACHED])
377                 return 0;
378
379         xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
380                 xdp_tb[IFLA_XDP_ATTACHED]);
381
382         if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
383                 return 0;
384
385         if (xdp_tb[IFLA_XDP_PROG_ID])
386                 xdp_id->info.prog_id = libbpf_nla_getattr_u32(
387                         xdp_tb[IFLA_XDP_PROG_ID]);
388
389         if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
390                 xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
391                         xdp_tb[IFLA_XDP_SKB_PROG_ID]);
392
393         if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
394                 xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
395                         xdp_tb[IFLA_XDP_DRV_PROG_ID]);
396
397         if (xdp_tb[IFLA_XDP_HW_PROG_ID])
398                 xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
399                         xdp_tb[IFLA_XDP_HW_PROG_ID]);
400
401         return 0;
402 }
403
404 static int parse_xdp_features(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
405                               void *cookie)
406 {
407         struct genlmsghdr *gnl = NLMSG_DATA(nh);
408         struct nlattr *na = (struct nlattr *)((void *)gnl + GENL_HDRLEN);
409         struct nlattr *tb[NETDEV_CMD_MAX + 1];
410         struct xdp_features_md *md = cookie;
411         __u32 ifindex;
412
413         libbpf_nla_parse(tb, NETDEV_CMD_MAX, na,
414                          NLMSG_PAYLOAD(nh, sizeof(*gnl)), NULL);
415
416         if (!tb[NETDEV_A_DEV_IFINDEX] || !tb[NETDEV_A_DEV_XDP_FEATURES])
417                 return NL_CONT;
418
419         ifindex = libbpf_nla_getattr_u32(tb[NETDEV_A_DEV_IFINDEX]);
420         if (ifindex != md->ifindex)
421                 return NL_CONT;
422
423         md->flags = libbpf_nla_getattr_u64(tb[NETDEV_A_DEV_XDP_FEATURES]);
424         return NL_DONE;
425 }
426
427 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts)
428 {
429         struct libbpf_nla_req req = {
430                 .nh.nlmsg_len      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
431                 .nh.nlmsg_type     = RTM_GETLINK,
432                 .nh.nlmsg_flags    = NLM_F_DUMP | NLM_F_REQUEST,
433                 .ifinfo.ifi_family = AF_PACKET,
434         };
435         struct xdp_id_md xdp_id = {};
436         struct xdp_features_md md = {
437                 .ifindex = ifindex,
438         };
439         __u16 id;
440         int err;
441
442         if (!OPTS_VALID(opts, bpf_xdp_query_opts))
443                 return libbpf_err(-EINVAL);
444
445         if (xdp_flags & ~XDP_FLAGS_MASK)
446                 return libbpf_err(-EINVAL);
447
448         /* Check whether the single {HW,DRV,SKB} mode is set */
449         xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE;
450         if (xdp_flags & (xdp_flags - 1))
451                 return libbpf_err(-EINVAL);
452
453         xdp_id.ifindex = ifindex;
454         xdp_id.flags = xdp_flags;
455
456         err = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, __dump_link_nlmsg,
457                                        get_xdp_info, &xdp_id);
458         if (err)
459                 return libbpf_err(err);
460
461         OPTS_SET(opts, prog_id, xdp_id.info.prog_id);
462         OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id);
463         OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id);
464         OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id);
465         OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode);
466
467         if (!OPTS_HAS(opts, feature_flags))
468                 return 0;
469
470         err = libbpf_netlink_resolve_genl_family_id("netdev", sizeof("netdev"), &id);
471         if (err < 0) {
472                 if (err == -ENOENT) {
473                         opts->feature_flags = 0;
474                         goto skip_feature_flags;
475                 }
476                 return libbpf_err(err);
477         }
478
479         memset(&req, 0, sizeof(req));
480         req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
481         req.nh.nlmsg_flags = NLM_F_REQUEST;
482         req.nh.nlmsg_type = id;
483         req.gnl.cmd = NETDEV_CMD_DEV_GET;
484         req.gnl.version = 2;
485
486         err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex));
487         if (err < 0)
488                 return libbpf_err(err);
489
490         err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC,
491                                        parse_xdp_features, NULL, &md);
492         if (err)
493                 return libbpf_err(err);
494
495         opts->feature_flags = md.flags;
496
497 skip_feature_flags:
498         return 0;
499 }
500
501 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id)
502 {
503         LIBBPF_OPTS(bpf_xdp_query_opts, opts);
504         int ret;
505
506         ret = bpf_xdp_query(ifindex, flags, &opts);
507         if (ret)
508                 return libbpf_err(ret);
509
510         flags &= XDP_FLAGS_MODES;
511
512         if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags)
513                 *prog_id = opts.prog_id;
514         else if (flags & XDP_FLAGS_DRV_MODE)
515                 *prog_id = opts.drv_prog_id;
516         else if (flags & XDP_FLAGS_HW_MODE)
517                 *prog_id = opts.hw_prog_id;
518         else if (flags & XDP_FLAGS_SKB_MODE)
519                 *prog_id = opts.skb_prog_id;
520         else
521                 *prog_id = 0;
522
523         return 0;
524 }
525
526
527 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req);
528
529 static int clsact_config(struct libbpf_nla_req *req)
530 {
531         req->tc.tcm_parent = TC_H_CLSACT;
532         req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
533
534         return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact"));
535 }
536
537 static int attach_point_to_config(struct bpf_tc_hook *hook,
538                                   qdisc_config_t *config)
539 {
540         switch (OPTS_GET(hook, attach_point, 0)) {
541         case BPF_TC_INGRESS:
542         case BPF_TC_EGRESS:
543         case BPF_TC_INGRESS | BPF_TC_EGRESS:
544                 if (OPTS_GET(hook, parent, 0))
545                         return -EINVAL;
546                 *config = &clsact_config;
547                 return 0;
548         case BPF_TC_CUSTOM:
549                 return -EOPNOTSUPP;
550         default:
551                 return -EINVAL;
552         }
553 }
554
555 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point,
556                              __u32 *parent)
557 {
558         switch (attach_point) {
559         case BPF_TC_INGRESS:
560         case BPF_TC_EGRESS:
561                 if (*parent)
562                         return -EINVAL;
563                 *parent = TC_H_MAKE(TC_H_CLSACT,
564                                     attach_point == BPF_TC_INGRESS ?
565                                     TC_H_MIN_INGRESS : TC_H_MIN_EGRESS);
566                 break;
567         case BPF_TC_CUSTOM:
568                 if (!*parent)
569                         return -EINVAL;
570                 break;
571         default:
572                 return -EINVAL;
573         }
574         return 0;
575 }
576
577 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags)
578 {
579         qdisc_config_t config;
580         int ret;
581         struct libbpf_nla_req req;
582
583         ret = attach_point_to_config(hook, &config);
584         if (ret < 0)
585                 return ret;
586
587         memset(&req, 0, sizeof(req));
588         req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
589         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
590         req.nh.nlmsg_type  = cmd;
591         req.tc.tcm_family  = AF_UNSPEC;
592         req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0);
593
594         ret = config(&req);
595         if (ret < 0)
596                 return ret;
597
598         return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
599 }
600
601 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook)
602 {
603         return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL);
604 }
605
606 static int tc_qdisc_delete(struct bpf_tc_hook *hook)
607 {
608         return tc_qdisc_modify(hook, RTM_DELQDISC, 0);
609 }
610
611 int bpf_tc_hook_create(struct bpf_tc_hook *hook)
612 {
613         int ret;
614
615         if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
616             OPTS_GET(hook, ifindex, 0) <= 0)
617                 return libbpf_err(-EINVAL);
618
619         ret = tc_qdisc_create_excl(hook);
620         return libbpf_err(ret);
621 }
622
623 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
624                            const struct bpf_tc_opts *opts,
625                            const bool flush);
626
627 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook)
628 {
629         if (!hook || !OPTS_VALID(hook, bpf_tc_hook) ||
630             OPTS_GET(hook, ifindex, 0) <= 0)
631                 return libbpf_err(-EINVAL);
632
633         switch (OPTS_GET(hook, attach_point, 0)) {
634         case BPF_TC_INGRESS:
635         case BPF_TC_EGRESS:
636                 return libbpf_err(__bpf_tc_detach(hook, NULL, true));
637         case BPF_TC_INGRESS | BPF_TC_EGRESS:
638                 return libbpf_err(tc_qdisc_delete(hook));
639         case BPF_TC_CUSTOM:
640                 return libbpf_err(-EOPNOTSUPP);
641         default:
642                 return libbpf_err(-EINVAL);
643         }
644 }
645
646 struct bpf_cb_ctx {
647         struct bpf_tc_opts *opts;
648         bool processed;
649 };
650
651 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb,
652                          bool unicast)
653 {
654         struct nlattr *tbb[TCA_BPF_MAX + 1];
655         struct bpf_cb_ctx *info = cookie;
656
657         if (!info || !info->opts)
658                 return -EINVAL;
659         if (unicast && info->processed)
660                 return -EINVAL;
661         if (!tb[TCA_OPTIONS])
662                 return NL_CONT;
663
664         libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL);
665         if (!tbb[TCA_BPF_ID])
666                 return -EINVAL;
667
668         OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID]));
669         OPTS_SET(info->opts, handle, tc->tcm_handle);
670         OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16);
671
672         info->processed = true;
673         return unicast ? NL_NEXT : NL_DONE;
674 }
675
676 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn,
677                        void *cookie)
678 {
679         struct tcmsg *tc = NLMSG_DATA(nh);
680         struct nlattr *tb[TCA_MAX + 1];
681
682         libbpf_nla_parse(tb, TCA_MAX,
683                          (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))),
684                          NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL);
685         if (!tb[TCA_KIND])
686                 return NL_CONT;
687         return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO);
688 }
689
690 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd)
691 {
692         struct bpf_prog_info info;
693         __u32 info_len = sizeof(info);
694         char name[256];
695         int len, ret;
696
697         memset(&info, 0, info_len);
698         ret = bpf_prog_get_info_by_fd(fd, &info, &info_len);
699         if (ret < 0)
700                 return ret;
701
702         ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd));
703         if (ret < 0)
704                 return ret;
705         len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id);
706         if (len < 0)
707                 return -errno;
708         if (len >= sizeof(name))
709                 return -ENAMETOOLONG;
710         return nlattr_add(req, TCA_BPF_NAME, name, len + 1);
711 }
712
713 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
714 {
715         __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags;
716         int ret, ifindex, attach_point, prog_fd;
717         struct bpf_cb_ctx info = {};
718         struct libbpf_nla_req req;
719         struct nlattr *nla;
720
721         if (!hook || !opts ||
722             !OPTS_VALID(hook, bpf_tc_hook) ||
723             !OPTS_VALID(opts, bpf_tc_opts))
724                 return libbpf_err(-EINVAL);
725
726         ifindex      = OPTS_GET(hook, ifindex, 0);
727         parent       = OPTS_GET(hook, parent, 0);
728         attach_point = OPTS_GET(hook, attach_point, 0);
729
730         handle       = OPTS_GET(opts, handle, 0);
731         priority     = OPTS_GET(opts, priority, 0);
732         prog_fd      = OPTS_GET(opts, prog_fd, 0);
733         prog_id      = OPTS_GET(opts, prog_id, 0);
734         flags        = OPTS_GET(opts, flags, 0);
735
736         if (ifindex <= 0 || !prog_fd || prog_id)
737                 return libbpf_err(-EINVAL);
738         if (priority > UINT16_MAX)
739                 return libbpf_err(-EINVAL);
740         if (flags & ~BPF_TC_F_REPLACE)
741                 return libbpf_err(-EINVAL);
742
743         flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL;
744         protocol = ETH_P_ALL;
745
746         memset(&req, 0, sizeof(req));
747         req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
748         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE |
749                              NLM_F_ECHO | flags;
750         req.nh.nlmsg_type  = RTM_NEWTFILTER;
751         req.tc.tcm_family  = AF_UNSPEC;
752         req.tc.tcm_ifindex = ifindex;
753         req.tc.tcm_handle  = handle;
754         req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
755
756         ret = tc_get_tcm_parent(attach_point, &parent);
757         if (ret < 0)
758                 return libbpf_err(ret);
759         req.tc.tcm_parent = parent;
760
761         ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
762         if (ret < 0)
763                 return libbpf_err(ret);
764         nla = nlattr_begin_nested(&req, TCA_OPTIONS);
765         if (!nla)
766                 return libbpf_err(-EMSGSIZE);
767         ret = tc_add_fd_and_name(&req, prog_fd);
768         if (ret < 0)
769                 return libbpf_err(ret);
770         bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
771         ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags));
772         if (ret < 0)
773                 return libbpf_err(ret);
774         nlattr_end_nested(&req, nla);
775
776         info.opts = opts;
777
778         ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
779                                        &info);
780         if (ret < 0)
781                 return libbpf_err(ret);
782         if (!info.processed)
783                 return libbpf_err(-ENOENT);
784         return ret;
785 }
786
787 static int __bpf_tc_detach(const struct bpf_tc_hook *hook,
788                            const struct bpf_tc_opts *opts,
789                            const bool flush)
790 {
791         __u32 protocol = 0, handle, priority, parent, prog_id, flags;
792         int ret, ifindex, attach_point, prog_fd;
793         struct libbpf_nla_req req;
794
795         if (!hook ||
796             !OPTS_VALID(hook, bpf_tc_hook) ||
797             !OPTS_VALID(opts, bpf_tc_opts))
798                 return -EINVAL;
799
800         ifindex      = OPTS_GET(hook, ifindex, 0);
801         parent       = OPTS_GET(hook, parent, 0);
802         attach_point = OPTS_GET(hook, attach_point, 0);
803
804         handle       = OPTS_GET(opts, handle, 0);
805         priority     = OPTS_GET(opts, priority, 0);
806         prog_fd      = OPTS_GET(opts, prog_fd, 0);
807         prog_id      = OPTS_GET(opts, prog_id, 0);
808         flags        = OPTS_GET(opts, flags, 0);
809
810         if (ifindex <= 0 || flags || prog_fd || prog_id)
811                 return -EINVAL;
812         if (priority > UINT16_MAX)
813                 return -EINVAL;
814         if (!flush) {
815                 if (!handle || !priority)
816                         return -EINVAL;
817                 protocol = ETH_P_ALL;
818         } else {
819                 if (handle || priority)
820                         return -EINVAL;
821         }
822
823         memset(&req, 0, sizeof(req));
824         req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
825         req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
826         req.nh.nlmsg_type  = RTM_DELTFILTER;
827         req.tc.tcm_family  = AF_UNSPEC;
828         req.tc.tcm_ifindex = ifindex;
829         if (!flush) {
830                 req.tc.tcm_handle = handle;
831                 req.tc.tcm_info   = TC_H_MAKE(priority << 16, htons(protocol));
832         }
833
834         ret = tc_get_tcm_parent(attach_point, &parent);
835         if (ret < 0)
836                 return ret;
837         req.tc.tcm_parent = parent;
838
839         if (!flush) {
840                 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
841                 if (ret < 0)
842                         return ret;
843         }
844
845         return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL);
846 }
847
848 int bpf_tc_detach(const struct bpf_tc_hook *hook,
849                   const struct bpf_tc_opts *opts)
850 {
851         int ret;
852
853         if (!opts)
854                 return libbpf_err(-EINVAL);
855
856         ret = __bpf_tc_detach(hook, opts, false);
857         return libbpf_err(ret);
858 }
859
860 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts)
861 {
862         __u32 protocol, handle, priority, parent, prog_id, flags;
863         int ret, ifindex, attach_point, prog_fd;
864         struct bpf_cb_ctx info = {};
865         struct libbpf_nla_req req;
866
867         if (!hook || !opts ||
868             !OPTS_VALID(hook, bpf_tc_hook) ||
869             !OPTS_VALID(opts, bpf_tc_opts))
870                 return libbpf_err(-EINVAL);
871
872         ifindex      = OPTS_GET(hook, ifindex, 0);
873         parent       = OPTS_GET(hook, parent, 0);
874         attach_point = OPTS_GET(hook, attach_point, 0);
875
876         handle       = OPTS_GET(opts, handle, 0);
877         priority     = OPTS_GET(opts, priority, 0);
878         prog_fd      = OPTS_GET(opts, prog_fd, 0);
879         prog_id      = OPTS_GET(opts, prog_id, 0);
880         flags        = OPTS_GET(opts, flags, 0);
881
882         if (ifindex <= 0 || flags || prog_fd || prog_id ||
883             !handle || !priority)
884                 return libbpf_err(-EINVAL);
885         if (priority > UINT16_MAX)
886                 return libbpf_err(-EINVAL);
887
888         protocol = ETH_P_ALL;
889
890         memset(&req, 0, sizeof(req));
891         req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct tcmsg));
892         req.nh.nlmsg_flags = NLM_F_REQUEST;
893         req.nh.nlmsg_type  = RTM_GETTFILTER;
894         req.tc.tcm_family  = AF_UNSPEC;
895         req.tc.tcm_ifindex = ifindex;
896         req.tc.tcm_handle  = handle;
897         req.tc.tcm_info    = TC_H_MAKE(priority << 16, htons(protocol));
898
899         ret = tc_get_tcm_parent(attach_point, &parent);
900         if (ret < 0)
901                 return libbpf_err(ret);
902         req.tc.tcm_parent = parent;
903
904         ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf"));
905         if (ret < 0)
906                 return libbpf_err(ret);
907
908         info.opts = opts;
909
910         ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL,
911                                        &info);
912         if (ret < 0)
913                 return libbpf_err(ret);
914         if (!info.processed)
915                 return libbpf_err(-ENOENT);
916         return ret;
917 }