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