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 25 typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); 26 27 typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t, 28 void *cookie); 29 30 struct xdp_id_md { 31 int ifindex; 32 __u32 flags; 33 struct xdp_link_info info; 34 }; 35 36 static 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 74 cleanup: 75 close(sock); 76 return ret; 77 } 78 79 static void libbpf_netlink_close(int sock) 80 { 81 close(sock); 82 } 83 84 enum { 85 NL_CONT, 86 NL_NEXT, 87 NL_DONE, 88 }; 89 90 static 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) { 101 start: 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; 153 done: 154 return ret; 155 } 156 157 static 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); 179 out: 180 libbpf_netlink_close(sock); 181 return ret; 182 } 183 184 static 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 220 int bpf_xdp_attach(int ifindex, int prog_fd, __u32 flags, const struct bpf_xdp_attach_opts *opts) 221 { 222 int old_prog_fd, err; 223 224 if (!OPTS_VALID(opts, bpf_xdp_attach_opts)) 225 return libbpf_err(-EINVAL); 226 227 old_prog_fd = OPTS_GET(opts, old_prog_fd, 0); 228 if (old_prog_fd) 229 flags |= XDP_FLAGS_REPLACE; 230 else 231 old_prog_fd = -1; 232 233 err = __bpf_set_link_xdp_fd_replace(ifindex, prog_fd, old_prog_fd, flags); 234 return libbpf_err(err); 235 } 236 237 int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *opts) 238 { 239 return bpf_xdp_attach(ifindex, -1, flags, opts); 240 } 241 242 int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags, 243 const struct bpf_xdp_set_link_opts *opts) 244 { 245 int old_fd = -1, ret; 246 247 if (!OPTS_VALID(opts, bpf_xdp_set_link_opts)) 248 return libbpf_err(-EINVAL); 249 250 if (OPTS_HAS(opts, old_fd)) { 251 old_fd = OPTS_GET(opts, old_fd, -1); 252 flags |= XDP_FLAGS_REPLACE; 253 } 254 255 ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, old_fd, flags); 256 return libbpf_err(ret); 257 } 258 259 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags) 260 { 261 int ret; 262 263 ret = __bpf_set_link_xdp_fd_replace(ifindex, fd, 0, flags); 264 return libbpf_err(ret); 265 } 266 267 static int __dump_link_nlmsg(struct nlmsghdr *nlh, 268 libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) 269 { 270 struct nlattr *tb[IFLA_MAX + 1], *attr; 271 struct ifinfomsg *ifi = NLMSG_DATA(nlh); 272 int len; 273 274 len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); 275 attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); 276 277 if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) 278 return -LIBBPF_ERRNO__NLPARSE; 279 280 return dump_link_nlmsg(cookie, ifi, tb); 281 } 282 283 static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb) 284 { 285 struct nlattr *xdp_tb[IFLA_XDP_MAX + 1]; 286 struct xdp_id_md *xdp_id = cookie; 287 struct ifinfomsg *ifinfo = msg; 288 int ret; 289 290 if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index) 291 return 0; 292 293 if (!tb[IFLA_XDP]) 294 return 0; 295 296 ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL); 297 if (ret) 298 return ret; 299 300 if (!xdp_tb[IFLA_XDP_ATTACHED]) 301 return 0; 302 303 xdp_id->info.attach_mode = libbpf_nla_getattr_u8( 304 xdp_tb[IFLA_XDP_ATTACHED]); 305 306 if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE) 307 return 0; 308 309 if (xdp_tb[IFLA_XDP_PROG_ID]) 310 xdp_id->info.prog_id = libbpf_nla_getattr_u32( 311 xdp_tb[IFLA_XDP_PROG_ID]); 312 313 if (xdp_tb[IFLA_XDP_SKB_PROG_ID]) 314 xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32( 315 xdp_tb[IFLA_XDP_SKB_PROG_ID]); 316 317 if (xdp_tb[IFLA_XDP_DRV_PROG_ID]) 318 xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32( 319 xdp_tb[IFLA_XDP_DRV_PROG_ID]); 320 321 if (xdp_tb[IFLA_XDP_HW_PROG_ID]) 322 xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32( 323 xdp_tb[IFLA_XDP_HW_PROG_ID]); 324 325 return 0; 326 } 327 328 int bpf_xdp_query(int ifindex, int xdp_flags, struct bpf_xdp_query_opts *opts) 329 { 330 struct libbpf_nla_req req = { 331 .nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), 332 .nh.nlmsg_type = RTM_GETLINK, 333 .nh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, 334 .ifinfo.ifi_family = AF_PACKET, 335 }; 336 struct xdp_id_md xdp_id = {}; 337 int err; 338 339 if (!OPTS_VALID(opts, bpf_xdp_query_opts)) 340 return libbpf_err(-EINVAL); 341 342 if (xdp_flags & ~XDP_FLAGS_MASK) 343 return libbpf_err(-EINVAL); 344 345 /* Check whether the single {HW,DRV,SKB} mode is set */ 346 xdp_flags &= XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE; 347 if (xdp_flags & (xdp_flags - 1)) 348 return libbpf_err(-EINVAL); 349 350 xdp_id.ifindex = ifindex; 351 xdp_id.flags = xdp_flags; 352 353 err = libbpf_netlink_send_recv(&req, __dump_link_nlmsg, 354 get_xdp_info, &xdp_id); 355 if (err) 356 return libbpf_err(err); 357 358 OPTS_SET(opts, prog_id, xdp_id.info.prog_id); 359 OPTS_SET(opts, drv_prog_id, xdp_id.info.drv_prog_id); 360 OPTS_SET(opts, hw_prog_id, xdp_id.info.hw_prog_id); 361 OPTS_SET(opts, skb_prog_id, xdp_id.info.skb_prog_id); 362 OPTS_SET(opts, attach_mode, xdp_id.info.attach_mode); 363 364 return 0; 365 } 366 367 int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info, 368 size_t info_size, __u32 flags) 369 { 370 LIBBPF_OPTS(bpf_xdp_query_opts, opts); 371 size_t sz; 372 int err; 373 374 if (!info_size) 375 return libbpf_err(-EINVAL); 376 377 err = bpf_xdp_query(ifindex, flags, &opts); 378 if (err) 379 return libbpf_err(err); 380 381 /* struct xdp_link_info field layout matches struct bpf_xdp_query_opts 382 * layout after sz field 383 */ 384 sz = min(info_size, offsetofend(struct xdp_link_info, attach_mode)); 385 memcpy(info, &opts.prog_id, sz); 386 memset((void *)info + sz, 0, info_size - sz); 387 388 return 0; 389 } 390 391 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id) 392 { 393 LIBBPF_OPTS(bpf_xdp_query_opts, opts); 394 int ret; 395 396 ret = bpf_xdp_query(ifindex, flags, &opts); 397 if (ret) 398 return libbpf_err(ret); 399 400 flags &= XDP_FLAGS_MODES; 401 402 if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags) 403 *prog_id = opts.prog_id; 404 else if (flags & XDP_FLAGS_DRV_MODE) 405 *prog_id = opts.drv_prog_id; 406 else if (flags & XDP_FLAGS_HW_MODE) 407 *prog_id = opts.hw_prog_id; 408 else if (flags & XDP_FLAGS_SKB_MODE) 409 *prog_id = opts.skb_prog_id; 410 else 411 *prog_id = 0; 412 413 return 0; 414 } 415 416 417 int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags) 418 { 419 return bpf_xdp_query_id(ifindex, flags, prog_id); 420 } 421 422 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req); 423 424 static int clsact_config(struct libbpf_nla_req *req) 425 { 426 req->tc.tcm_parent = TC_H_CLSACT; 427 req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); 428 429 return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact")); 430 } 431 432 static int attach_point_to_config(struct bpf_tc_hook *hook, 433 qdisc_config_t *config) 434 { 435 switch (OPTS_GET(hook, attach_point, 0)) { 436 case BPF_TC_INGRESS: 437 case BPF_TC_EGRESS: 438 case BPF_TC_INGRESS | BPF_TC_EGRESS: 439 if (OPTS_GET(hook, parent, 0)) 440 return -EINVAL; 441 *config = &clsact_config; 442 return 0; 443 case BPF_TC_CUSTOM: 444 return -EOPNOTSUPP; 445 default: 446 return -EINVAL; 447 } 448 } 449 450 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point, 451 __u32 *parent) 452 { 453 switch (attach_point) { 454 case BPF_TC_INGRESS: 455 case BPF_TC_EGRESS: 456 if (*parent) 457 return -EINVAL; 458 *parent = TC_H_MAKE(TC_H_CLSACT, 459 attach_point == BPF_TC_INGRESS ? 460 TC_H_MIN_INGRESS : TC_H_MIN_EGRESS); 461 break; 462 case BPF_TC_CUSTOM: 463 if (!*parent) 464 return -EINVAL; 465 break; 466 default: 467 return -EINVAL; 468 } 469 return 0; 470 } 471 472 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags) 473 { 474 qdisc_config_t config; 475 int ret; 476 struct libbpf_nla_req req; 477 478 ret = attach_point_to_config(hook, &config); 479 if (ret < 0) 480 return ret; 481 482 memset(&req, 0, sizeof(req)); 483 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 484 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; 485 req.nh.nlmsg_type = cmd; 486 req.tc.tcm_family = AF_UNSPEC; 487 req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0); 488 489 ret = config(&req); 490 if (ret < 0) 491 return ret; 492 493 return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); 494 } 495 496 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook) 497 { 498 return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL); 499 } 500 501 static int tc_qdisc_delete(struct bpf_tc_hook *hook) 502 { 503 return tc_qdisc_modify(hook, RTM_DELQDISC, 0); 504 } 505 506 int bpf_tc_hook_create(struct bpf_tc_hook *hook) 507 { 508 int ret; 509 510 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 511 OPTS_GET(hook, ifindex, 0) <= 0) 512 return libbpf_err(-EINVAL); 513 514 ret = tc_qdisc_create_excl(hook); 515 return libbpf_err(ret); 516 } 517 518 static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 519 const struct bpf_tc_opts *opts, 520 const bool flush); 521 522 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook) 523 { 524 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 525 OPTS_GET(hook, ifindex, 0) <= 0) 526 return libbpf_err(-EINVAL); 527 528 switch (OPTS_GET(hook, attach_point, 0)) { 529 case BPF_TC_INGRESS: 530 case BPF_TC_EGRESS: 531 return libbpf_err(__bpf_tc_detach(hook, NULL, true)); 532 case BPF_TC_INGRESS | BPF_TC_EGRESS: 533 return libbpf_err(tc_qdisc_delete(hook)); 534 case BPF_TC_CUSTOM: 535 return libbpf_err(-EOPNOTSUPP); 536 default: 537 return libbpf_err(-EINVAL); 538 } 539 } 540 541 struct bpf_cb_ctx { 542 struct bpf_tc_opts *opts; 543 bool processed; 544 }; 545 546 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb, 547 bool unicast) 548 { 549 struct nlattr *tbb[TCA_BPF_MAX + 1]; 550 struct bpf_cb_ctx *info = cookie; 551 552 if (!info || !info->opts) 553 return -EINVAL; 554 if (unicast && info->processed) 555 return -EINVAL; 556 if (!tb[TCA_OPTIONS]) 557 return NL_CONT; 558 559 libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL); 560 if (!tbb[TCA_BPF_ID]) 561 return -EINVAL; 562 563 OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID])); 564 OPTS_SET(info->opts, handle, tc->tcm_handle); 565 OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16); 566 567 info->processed = true; 568 return unicast ? NL_NEXT : NL_DONE; 569 } 570 571 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 572 void *cookie) 573 { 574 struct tcmsg *tc = NLMSG_DATA(nh); 575 struct nlattr *tb[TCA_MAX + 1]; 576 577 libbpf_nla_parse(tb, TCA_MAX, 578 (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))), 579 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL); 580 if (!tb[TCA_KIND]) 581 return NL_CONT; 582 return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO); 583 } 584 585 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd) 586 { 587 struct bpf_prog_info info = {}; 588 __u32 info_len = sizeof(info); 589 char name[256]; 590 int len, ret; 591 592 ret = bpf_obj_get_info_by_fd(fd, &info, &info_len); 593 if (ret < 0) 594 return ret; 595 596 ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd)); 597 if (ret < 0) 598 return ret; 599 len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id); 600 if (len < 0) 601 return -errno; 602 if (len >= sizeof(name)) 603 return -ENAMETOOLONG; 604 return nlattr_add(req, TCA_BPF_NAME, name, len + 1); 605 } 606 607 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 608 { 609 __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags; 610 int ret, ifindex, attach_point, prog_fd; 611 struct bpf_cb_ctx info = {}; 612 struct libbpf_nla_req req; 613 struct nlattr *nla; 614 615 if (!hook || !opts || 616 !OPTS_VALID(hook, bpf_tc_hook) || 617 !OPTS_VALID(opts, bpf_tc_opts)) 618 return libbpf_err(-EINVAL); 619 620 ifindex = OPTS_GET(hook, ifindex, 0); 621 parent = OPTS_GET(hook, parent, 0); 622 attach_point = OPTS_GET(hook, attach_point, 0); 623 624 handle = OPTS_GET(opts, handle, 0); 625 priority = OPTS_GET(opts, priority, 0); 626 prog_fd = OPTS_GET(opts, prog_fd, 0); 627 prog_id = OPTS_GET(opts, prog_id, 0); 628 flags = OPTS_GET(opts, flags, 0); 629 630 if (ifindex <= 0 || !prog_fd || prog_id) 631 return libbpf_err(-EINVAL); 632 if (priority > UINT16_MAX) 633 return libbpf_err(-EINVAL); 634 if (flags & ~BPF_TC_F_REPLACE) 635 return libbpf_err(-EINVAL); 636 637 flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL; 638 protocol = ETH_P_ALL; 639 640 memset(&req, 0, sizeof(req)); 641 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 642 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | 643 NLM_F_ECHO | flags; 644 req.nh.nlmsg_type = RTM_NEWTFILTER; 645 req.tc.tcm_family = AF_UNSPEC; 646 req.tc.tcm_ifindex = ifindex; 647 req.tc.tcm_handle = handle; 648 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 649 650 ret = tc_get_tcm_parent(attach_point, &parent); 651 if (ret < 0) 652 return libbpf_err(ret); 653 req.tc.tcm_parent = parent; 654 655 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 656 if (ret < 0) 657 return libbpf_err(ret); 658 nla = nlattr_begin_nested(&req, TCA_OPTIONS); 659 if (!nla) 660 return libbpf_err(-EMSGSIZE); 661 ret = tc_add_fd_and_name(&req, prog_fd); 662 if (ret < 0) 663 return libbpf_err(ret); 664 bpf_flags = TCA_BPF_FLAG_ACT_DIRECT; 665 ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags)); 666 if (ret < 0) 667 return libbpf_err(ret); 668 nlattr_end_nested(&req, nla); 669 670 info.opts = opts; 671 672 ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info); 673 if (ret < 0) 674 return libbpf_err(ret); 675 if (!info.processed) 676 return libbpf_err(-ENOENT); 677 return ret; 678 } 679 680 static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 681 const struct bpf_tc_opts *opts, 682 const bool flush) 683 { 684 __u32 protocol = 0, handle, priority, parent, prog_id, flags; 685 int ret, ifindex, attach_point, prog_fd; 686 struct libbpf_nla_req req; 687 688 if (!hook || 689 !OPTS_VALID(hook, bpf_tc_hook) || 690 !OPTS_VALID(opts, bpf_tc_opts)) 691 return -EINVAL; 692 693 ifindex = OPTS_GET(hook, ifindex, 0); 694 parent = OPTS_GET(hook, parent, 0); 695 attach_point = OPTS_GET(hook, attach_point, 0); 696 697 handle = OPTS_GET(opts, handle, 0); 698 priority = OPTS_GET(opts, priority, 0); 699 prog_fd = OPTS_GET(opts, prog_fd, 0); 700 prog_id = OPTS_GET(opts, prog_id, 0); 701 flags = OPTS_GET(opts, flags, 0); 702 703 if (ifindex <= 0 || flags || prog_fd || prog_id) 704 return -EINVAL; 705 if (priority > UINT16_MAX) 706 return -EINVAL; 707 if (!flush) { 708 if (!handle || !priority) 709 return -EINVAL; 710 protocol = ETH_P_ALL; 711 } else { 712 if (handle || priority) 713 return -EINVAL; 714 } 715 716 memset(&req, 0, sizeof(req)); 717 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 718 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 719 req.nh.nlmsg_type = RTM_DELTFILTER; 720 req.tc.tcm_family = AF_UNSPEC; 721 req.tc.tcm_ifindex = ifindex; 722 if (!flush) { 723 req.tc.tcm_handle = handle; 724 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 725 } 726 727 ret = tc_get_tcm_parent(attach_point, &parent); 728 if (ret < 0) 729 return ret; 730 req.tc.tcm_parent = parent; 731 732 if (!flush) { 733 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 734 if (ret < 0) 735 return ret; 736 } 737 738 return libbpf_netlink_send_recv(&req, NULL, NULL, NULL); 739 } 740 741 int bpf_tc_detach(const struct bpf_tc_hook *hook, 742 const struct bpf_tc_opts *opts) 743 { 744 int ret; 745 746 if (!opts) 747 return libbpf_err(-EINVAL); 748 749 ret = __bpf_tc_detach(hook, opts, false); 750 return libbpf_err(ret); 751 } 752 753 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 754 { 755 __u32 protocol, handle, priority, parent, prog_id, flags; 756 int ret, ifindex, attach_point, prog_fd; 757 struct bpf_cb_ctx info = {}; 758 struct libbpf_nla_req req; 759 760 if (!hook || !opts || 761 !OPTS_VALID(hook, bpf_tc_hook) || 762 !OPTS_VALID(opts, bpf_tc_opts)) 763 return libbpf_err(-EINVAL); 764 765 ifindex = OPTS_GET(hook, ifindex, 0); 766 parent = OPTS_GET(hook, parent, 0); 767 attach_point = OPTS_GET(hook, attach_point, 0); 768 769 handle = OPTS_GET(opts, handle, 0); 770 priority = OPTS_GET(opts, priority, 0); 771 prog_fd = OPTS_GET(opts, prog_fd, 0); 772 prog_id = OPTS_GET(opts, prog_id, 0); 773 flags = OPTS_GET(opts, flags, 0); 774 775 if (ifindex <= 0 || flags || prog_fd || prog_id || 776 !handle || !priority) 777 return libbpf_err(-EINVAL); 778 if (priority > UINT16_MAX) 779 return libbpf_err(-EINVAL); 780 781 protocol = ETH_P_ALL; 782 783 memset(&req, 0, sizeof(req)); 784 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 785 req.nh.nlmsg_flags = NLM_F_REQUEST; 786 req.nh.nlmsg_type = RTM_GETTFILTER; 787 req.tc.tcm_family = AF_UNSPEC; 788 req.tc.tcm_ifindex = ifindex; 789 req.tc.tcm_handle = handle; 790 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 791 792 ret = tc_get_tcm_parent(attach_point, &parent); 793 if (ret < 0) 794 return libbpf_err(ret); 795 req.tc.tcm_parent = parent; 796 797 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 798 if (ret < 0) 799 return libbpf_err(ret); 800 801 info.opts = opts; 802 803 ret = libbpf_netlink_send_recv(&req, get_tc_info, NULL, &info); 804 if (ret < 0) 805 return libbpf_err(ret); 806 if (!info.processed) 807 return libbpf_err(-ENOENT); 808 return ret; 809 } 810