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 return libbpf_err(err); 473 474 memset(&req, 0, sizeof(req)); 475 req.nh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); 476 req.nh.nlmsg_flags = NLM_F_REQUEST; 477 req.nh.nlmsg_type = id; 478 req.gnl.cmd = NETDEV_CMD_DEV_GET; 479 req.gnl.version = 2; 480 481 err = nlattr_add(&req, NETDEV_A_DEV_IFINDEX, &ifindex, sizeof(ifindex)); 482 if (err < 0) 483 return libbpf_err(err); 484 485 err = libbpf_netlink_send_recv(&req, NETLINK_GENERIC, 486 parse_xdp_features, NULL, &md); 487 if (err) 488 return libbpf_err(err); 489 490 opts->feature_flags = md.flags; 491 492 return 0; 493 } 494 495 int bpf_xdp_query_id(int ifindex, int flags, __u32 *prog_id) 496 { 497 LIBBPF_OPTS(bpf_xdp_query_opts, opts); 498 int ret; 499 500 ret = bpf_xdp_query(ifindex, flags, &opts); 501 if (ret) 502 return libbpf_err(ret); 503 504 flags &= XDP_FLAGS_MODES; 505 506 if (opts.attach_mode != XDP_ATTACHED_MULTI && !flags) 507 *prog_id = opts.prog_id; 508 else if (flags & XDP_FLAGS_DRV_MODE) 509 *prog_id = opts.drv_prog_id; 510 else if (flags & XDP_FLAGS_HW_MODE) 511 *prog_id = opts.hw_prog_id; 512 else if (flags & XDP_FLAGS_SKB_MODE) 513 *prog_id = opts.skb_prog_id; 514 else 515 *prog_id = 0; 516 517 return 0; 518 } 519 520 521 typedef int (*qdisc_config_t)(struct libbpf_nla_req *req); 522 523 static int clsact_config(struct libbpf_nla_req *req) 524 { 525 req->tc.tcm_parent = TC_H_CLSACT; 526 req->tc.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0); 527 528 return nlattr_add(req, TCA_KIND, "clsact", sizeof("clsact")); 529 } 530 531 static int attach_point_to_config(struct bpf_tc_hook *hook, 532 qdisc_config_t *config) 533 { 534 switch (OPTS_GET(hook, attach_point, 0)) { 535 case BPF_TC_INGRESS: 536 case BPF_TC_EGRESS: 537 case BPF_TC_INGRESS | BPF_TC_EGRESS: 538 if (OPTS_GET(hook, parent, 0)) 539 return -EINVAL; 540 *config = &clsact_config; 541 return 0; 542 case BPF_TC_CUSTOM: 543 return -EOPNOTSUPP; 544 default: 545 return -EINVAL; 546 } 547 } 548 549 static int tc_get_tcm_parent(enum bpf_tc_attach_point attach_point, 550 __u32 *parent) 551 { 552 switch (attach_point) { 553 case BPF_TC_INGRESS: 554 case BPF_TC_EGRESS: 555 if (*parent) 556 return -EINVAL; 557 *parent = TC_H_MAKE(TC_H_CLSACT, 558 attach_point == BPF_TC_INGRESS ? 559 TC_H_MIN_INGRESS : TC_H_MIN_EGRESS); 560 break; 561 case BPF_TC_CUSTOM: 562 if (!*parent) 563 return -EINVAL; 564 break; 565 default: 566 return -EINVAL; 567 } 568 return 0; 569 } 570 571 static int tc_qdisc_modify(struct bpf_tc_hook *hook, int cmd, int flags) 572 { 573 qdisc_config_t config; 574 int ret; 575 struct libbpf_nla_req req; 576 577 ret = attach_point_to_config(hook, &config); 578 if (ret < 0) 579 return ret; 580 581 memset(&req, 0, sizeof(req)); 582 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 583 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; 584 req.nh.nlmsg_type = cmd; 585 req.tc.tcm_family = AF_UNSPEC; 586 req.tc.tcm_ifindex = OPTS_GET(hook, ifindex, 0); 587 588 ret = config(&req); 589 if (ret < 0) 590 return ret; 591 592 return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL); 593 } 594 595 static int tc_qdisc_create_excl(struct bpf_tc_hook *hook) 596 { 597 return tc_qdisc_modify(hook, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL); 598 } 599 600 static int tc_qdisc_delete(struct bpf_tc_hook *hook) 601 { 602 return tc_qdisc_modify(hook, RTM_DELQDISC, 0); 603 } 604 605 int bpf_tc_hook_create(struct bpf_tc_hook *hook) 606 { 607 int ret; 608 609 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 610 OPTS_GET(hook, ifindex, 0) <= 0) 611 return libbpf_err(-EINVAL); 612 613 ret = tc_qdisc_create_excl(hook); 614 return libbpf_err(ret); 615 } 616 617 static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 618 const struct bpf_tc_opts *opts, 619 const bool flush); 620 621 int bpf_tc_hook_destroy(struct bpf_tc_hook *hook) 622 { 623 if (!hook || !OPTS_VALID(hook, bpf_tc_hook) || 624 OPTS_GET(hook, ifindex, 0) <= 0) 625 return libbpf_err(-EINVAL); 626 627 switch (OPTS_GET(hook, attach_point, 0)) { 628 case BPF_TC_INGRESS: 629 case BPF_TC_EGRESS: 630 return libbpf_err(__bpf_tc_detach(hook, NULL, true)); 631 case BPF_TC_INGRESS | BPF_TC_EGRESS: 632 return libbpf_err(tc_qdisc_delete(hook)); 633 case BPF_TC_CUSTOM: 634 return libbpf_err(-EOPNOTSUPP); 635 default: 636 return libbpf_err(-EINVAL); 637 } 638 } 639 640 struct bpf_cb_ctx { 641 struct bpf_tc_opts *opts; 642 bool processed; 643 }; 644 645 static int __get_tc_info(void *cookie, struct tcmsg *tc, struct nlattr **tb, 646 bool unicast) 647 { 648 struct nlattr *tbb[TCA_BPF_MAX + 1]; 649 struct bpf_cb_ctx *info = cookie; 650 651 if (!info || !info->opts) 652 return -EINVAL; 653 if (unicast && info->processed) 654 return -EINVAL; 655 if (!tb[TCA_OPTIONS]) 656 return NL_CONT; 657 658 libbpf_nla_parse_nested(tbb, TCA_BPF_MAX, tb[TCA_OPTIONS], NULL); 659 if (!tbb[TCA_BPF_ID]) 660 return -EINVAL; 661 662 OPTS_SET(info->opts, prog_id, libbpf_nla_getattr_u32(tbb[TCA_BPF_ID])); 663 OPTS_SET(info->opts, handle, tc->tcm_handle); 664 OPTS_SET(info->opts, priority, TC_H_MAJ(tc->tcm_info) >> 16); 665 666 info->processed = true; 667 return unicast ? NL_NEXT : NL_DONE; 668 } 669 670 static int get_tc_info(struct nlmsghdr *nh, libbpf_dump_nlmsg_t fn, 671 void *cookie) 672 { 673 struct tcmsg *tc = NLMSG_DATA(nh); 674 struct nlattr *tb[TCA_MAX + 1]; 675 676 libbpf_nla_parse(tb, TCA_MAX, 677 (struct nlattr *)((void *)tc + NLMSG_ALIGN(sizeof(*tc))), 678 NLMSG_PAYLOAD(nh, sizeof(*tc)), NULL); 679 if (!tb[TCA_KIND]) 680 return NL_CONT; 681 return __get_tc_info(cookie, tc, tb, nh->nlmsg_flags & NLM_F_ECHO); 682 } 683 684 static int tc_add_fd_and_name(struct libbpf_nla_req *req, int fd) 685 { 686 struct bpf_prog_info info; 687 __u32 info_len = sizeof(info); 688 char name[256]; 689 int len, ret; 690 691 memset(&info, 0, info_len); 692 ret = bpf_prog_get_info_by_fd(fd, &info, &info_len); 693 if (ret < 0) 694 return ret; 695 696 ret = nlattr_add(req, TCA_BPF_FD, &fd, sizeof(fd)); 697 if (ret < 0) 698 return ret; 699 len = snprintf(name, sizeof(name), "%s:[%u]", info.name, info.id); 700 if (len < 0) 701 return -errno; 702 if (len >= sizeof(name)) 703 return -ENAMETOOLONG; 704 return nlattr_add(req, TCA_BPF_NAME, name, len + 1); 705 } 706 707 int bpf_tc_attach(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 708 { 709 __u32 protocol, bpf_flags, handle, priority, parent, prog_id, flags; 710 int ret, ifindex, attach_point, prog_fd; 711 struct bpf_cb_ctx info = {}; 712 struct libbpf_nla_req req; 713 struct nlattr *nla; 714 715 if (!hook || !opts || 716 !OPTS_VALID(hook, bpf_tc_hook) || 717 !OPTS_VALID(opts, bpf_tc_opts)) 718 return libbpf_err(-EINVAL); 719 720 ifindex = OPTS_GET(hook, ifindex, 0); 721 parent = OPTS_GET(hook, parent, 0); 722 attach_point = OPTS_GET(hook, attach_point, 0); 723 724 handle = OPTS_GET(opts, handle, 0); 725 priority = OPTS_GET(opts, priority, 0); 726 prog_fd = OPTS_GET(opts, prog_fd, 0); 727 prog_id = OPTS_GET(opts, prog_id, 0); 728 flags = OPTS_GET(opts, flags, 0); 729 730 if (ifindex <= 0 || !prog_fd || prog_id) 731 return libbpf_err(-EINVAL); 732 if (priority > UINT16_MAX) 733 return libbpf_err(-EINVAL); 734 if (flags & ~BPF_TC_F_REPLACE) 735 return libbpf_err(-EINVAL); 736 737 flags = (flags & BPF_TC_F_REPLACE) ? NLM_F_REPLACE : NLM_F_EXCL; 738 protocol = ETH_P_ALL; 739 740 memset(&req, 0, sizeof(req)); 741 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 742 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | 743 NLM_F_ECHO | flags; 744 req.nh.nlmsg_type = RTM_NEWTFILTER; 745 req.tc.tcm_family = AF_UNSPEC; 746 req.tc.tcm_ifindex = ifindex; 747 req.tc.tcm_handle = handle; 748 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 749 750 ret = tc_get_tcm_parent(attach_point, &parent); 751 if (ret < 0) 752 return libbpf_err(ret); 753 req.tc.tcm_parent = parent; 754 755 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 756 if (ret < 0) 757 return libbpf_err(ret); 758 nla = nlattr_begin_nested(&req, TCA_OPTIONS); 759 if (!nla) 760 return libbpf_err(-EMSGSIZE); 761 ret = tc_add_fd_and_name(&req, prog_fd); 762 if (ret < 0) 763 return libbpf_err(ret); 764 bpf_flags = TCA_BPF_FLAG_ACT_DIRECT; 765 ret = nlattr_add(&req, TCA_BPF_FLAGS, &bpf_flags, sizeof(bpf_flags)); 766 if (ret < 0) 767 return libbpf_err(ret); 768 nlattr_end_nested(&req, nla); 769 770 info.opts = opts; 771 772 ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL, 773 &info); 774 if (ret < 0) 775 return libbpf_err(ret); 776 if (!info.processed) 777 return libbpf_err(-ENOENT); 778 return ret; 779 } 780 781 static int __bpf_tc_detach(const struct bpf_tc_hook *hook, 782 const struct bpf_tc_opts *opts, 783 const bool flush) 784 { 785 __u32 protocol = 0, handle, priority, parent, prog_id, flags; 786 int ret, ifindex, attach_point, prog_fd; 787 struct libbpf_nla_req req; 788 789 if (!hook || 790 !OPTS_VALID(hook, bpf_tc_hook) || 791 !OPTS_VALID(opts, bpf_tc_opts)) 792 return -EINVAL; 793 794 ifindex = OPTS_GET(hook, ifindex, 0); 795 parent = OPTS_GET(hook, parent, 0); 796 attach_point = OPTS_GET(hook, attach_point, 0); 797 798 handle = OPTS_GET(opts, handle, 0); 799 priority = OPTS_GET(opts, priority, 0); 800 prog_fd = OPTS_GET(opts, prog_fd, 0); 801 prog_id = OPTS_GET(opts, prog_id, 0); 802 flags = OPTS_GET(opts, flags, 0); 803 804 if (ifindex <= 0 || flags || prog_fd || prog_id) 805 return -EINVAL; 806 if (priority > UINT16_MAX) 807 return -EINVAL; 808 if (!flush) { 809 if (!handle || !priority) 810 return -EINVAL; 811 protocol = ETH_P_ALL; 812 } else { 813 if (handle || priority) 814 return -EINVAL; 815 } 816 817 memset(&req, 0, sizeof(req)); 818 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 819 req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 820 req.nh.nlmsg_type = RTM_DELTFILTER; 821 req.tc.tcm_family = AF_UNSPEC; 822 req.tc.tcm_ifindex = ifindex; 823 if (!flush) { 824 req.tc.tcm_handle = handle; 825 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 826 } 827 828 ret = tc_get_tcm_parent(attach_point, &parent); 829 if (ret < 0) 830 return ret; 831 req.tc.tcm_parent = parent; 832 833 if (!flush) { 834 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 835 if (ret < 0) 836 return ret; 837 } 838 839 return libbpf_netlink_send_recv(&req, NETLINK_ROUTE, NULL, NULL, NULL); 840 } 841 842 int bpf_tc_detach(const struct bpf_tc_hook *hook, 843 const struct bpf_tc_opts *opts) 844 { 845 int ret; 846 847 if (!opts) 848 return libbpf_err(-EINVAL); 849 850 ret = __bpf_tc_detach(hook, opts, false); 851 return libbpf_err(ret); 852 } 853 854 int bpf_tc_query(const struct bpf_tc_hook *hook, struct bpf_tc_opts *opts) 855 { 856 __u32 protocol, handle, priority, parent, prog_id, flags; 857 int ret, ifindex, attach_point, prog_fd; 858 struct bpf_cb_ctx info = {}; 859 struct libbpf_nla_req req; 860 861 if (!hook || !opts || 862 !OPTS_VALID(hook, bpf_tc_hook) || 863 !OPTS_VALID(opts, bpf_tc_opts)) 864 return libbpf_err(-EINVAL); 865 866 ifindex = OPTS_GET(hook, ifindex, 0); 867 parent = OPTS_GET(hook, parent, 0); 868 attach_point = OPTS_GET(hook, attach_point, 0); 869 870 handle = OPTS_GET(opts, handle, 0); 871 priority = OPTS_GET(opts, priority, 0); 872 prog_fd = OPTS_GET(opts, prog_fd, 0); 873 prog_id = OPTS_GET(opts, prog_id, 0); 874 flags = OPTS_GET(opts, flags, 0); 875 876 if (ifindex <= 0 || flags || prog_fd || prog_id || 877 !handle || !priority) 878 return libbpf_err(-EINVAL); 879 if (priority > UINT16_MAX) 880 return libbpf_err(-EINVAL); 881 882 protocol = ETH_P_ALL; 883 884 memset(&req, 0, sizeof(req)); 885 req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); 886 req.nh.nlmsg_flags = NLM_F_REQUEST; 887 req.nh.nlmsg_type = RTM_GETTFILTER; 888 req.tc.tcm_family = AF_UNSPEC; 889 req.tc.tcm_ifindex = ifindex; 890 req.tc.tcm_handle = handle; 891 req.tc.tcm_info = TC_H_MAKE(priority << 16, htons(protocol)); 892 893 ret = tc_get_tcm_parent(attach_point, &parent); 894 if (ret < 0) 895 return libbpf_err(ret); 896 req.tc.tcm_parent = parent; 897 898 ret = nlattr_add(&req, TCA_KIND, "bpf", sizeof("bpf")); 899 if (ret < 0) 900 return libbpf_err(ret); 901 902 info.opts = opts; 903 904 ret = libbpf_netlink_send_recv(&req, NETLINK_ROUTE, get_tc_info, NULL, 905 &info); 906 if (ret < 0) 907 return libbpf_err(ret); 908 if (!info.processed) 909 return libbpf_err(-ENOENT); 910 return ret; 911 } 912