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