From: Felix Fietkau Date: Tue, 5 Apr 2022 19:25:29 +0000 (+0200) Subject: loader/interface: attach bpf program directly using netlink X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=1cd5e12eecdcab0845eda71383c4c0ba17092c6f;p=project%2Fqosify.git loader/interface: attach bpf program directly using netlink This makes it possible to replace the tc-full/tc-bpf dependency with a simple tc dependency. Signed-off-by: Felix Fietkau --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 3282866..8a8ea40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,9 +6,18 @@ ADD_DEFINITIONS(-Os -Wall -Wno-unknown-warning-option -Wno-array-bounds -Wno-for SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +IF (NOT DEFINED LIBNL_LIBS) + include(FindPkgConfig) + pkg_search_module(LIBNL libnl-3.0 libnl-3 libnl nl-3 nl) + IF (LIBNL_FOUND) + include_directories(${LIBNL_INCLUDE_DIRS}) + SET(LIBNL_LIBS ${LIBNL_LIBRARIES}) + ENDIF() +ENDIF() + find_library(bpf NAMES bpf) ADD_EXECUTABLE(qosify main.c loader.c map.c ubus.c interface.c dns.c) -TARGET_LINK_LIBRARIES(qosify ${bpf} ubox ubus) +TARGET_LINK_LIBRARIES(qosify ${bpf} ubox ubus ${LIBNL_LIBS}) INSTALL(TARGETS qosify RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} diff --git a/interface.c b/interface.c index 46e36a3..7979b21 100644 --- a/interface.c +++ b/interface.c @@ -2,15 +2,24 @@ /* * Copyright (C) 2021 Felix Fietkau */ +#define _GNU_SOURCE #include #include #include #include #include +#include #include #include +#include +#include +#include + +#include +#include + #include #include #include @@ -24,6 +33,7 @@ static void interface_update_cb(struct vlist_tree *tree, static VLIST_TREE(devices, avl_strcmp, interface_update_cb, true, false); static VLIST_TREE(interfaces, avl_strcmp, interface_update_cb, true, false); static int socket_fd; +static struct nl_sock *rtnl_sock; #define APPEND(_buf, _ofs, _format, ...) _ofs += snprintf(_buf + _ofs, sizeof(_buf) - _ofs, _format, ##__VA_ARGS__) @@ -211,15 +221,44 @@ prepare_filter_cmd(char *buf, int len, const char *dev, int prio, bool add, bool static int cmd_add_bpf_filter(const char *ifname, int prio, bool egress, bool eth) { - char buf[512]; - int ofs; + struct tcmsg tcmsg = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = if_nametoindex(ifname), + }; + struct nl_msg *msg; + struct nlattr *opts; + const char *suffix; + int prog_fd = -1; + char name[32]; + + suffix = qosify_get_program(!egress * QOSIFY_INGRESS + !eth * QOSIFY_IP_ONLY, &prog_fd); + if (!suffix) + return -1; - ofs = prepare_filter_cmd(buf, sizeof(buf), ifname, prio, true, egress); - APPEND(buf, ofs, " bpf object-pinned /sys/fs/bpf/qosify_%sgress_%s verbose direct-action", - egress ? "e" : "in", - eth ? "eth" : "ip"); + snprintf(name, sizeof(name), "qosify_%s", suffix); - return qosify_run_cmd(buf, false); + if (egress) + tcmsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS); + else + tcmsg.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); + + tcmsg.tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL)); + + msg = nlmsg_alloc_simple(RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); + nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO); + nla_put_string(msg, TCA_KIND, "bpf"); + + opts = nla_nest_start(msg, TCA_OPTIONS); + nla_put_u32(msg, TCA_BPF_FD, prog_fd); + nla_put_string(msg, TCA_BPF_NAME, name); + nla_put_u32(msg, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT); + nla_put_u32(msg, TCA_BPF_FLAGS_GEN, TCA_CLS_FLAGS_SKIP_HW); + nla_nest_end(msg, opts); + + nl_send_auto_complete(rtnl_sock, msg); + nlmsg_free(msg); + + return nl_wait_for_ack(rtnl_sock); } static int @@ -534,12 +573,60 @@ void qosify_iface_status(struct blob_buf *b) blobmsg_close_table(b, c); } +static int +qosify_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; + struct nlattr *attrs; + int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); + int len = nlh->nlmsg_len; + const char *errstr = "(unknown)"; + + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return NL_STOP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + ack_len += err->msg.nlmsg_len - sizeof(*nlh); + + attrs = (void *) ((unsigned char *) nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) + errstr = nla_data(tb[NLMSGERR_ATTR_MSG]); + + ULOG_ERR("Netlink error(%d): %s\n", err->error, errstr); + + return NL_STOP; +} + int qosify_iface_init(void) { + int fd, opt; + socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); if (socket < 0) return -1; + rtnl_sock = nl_socket_alloc(); + if (!rtnl_sock) + return -1; + + if (nl_connect(rtnl_sock, NETLINK_ROUTE)) + return -1; + + nl_cb_err(nl_socket_get_cb(rtnl_sock), NL_CB_CUSTOM, + qosify_nl_error_cb, NULL); + + fd = nl_socket_get_fd(rtnl_sock); + opt = 1; + setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt)); + + opt = 1; + setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt)); + return 0; } @@ -551,5 +638,7 @@ void qosify_iface_stop(void) interface_stop(iface); vlist_for_each_element(&devices, iface, node) interface_stop(iface); + + nl_socket_free(rtnl_sock); } diff --git a/loader.c b/loader.c index c50e739..64797f0 100644 --- a/loader.c +++ b/loader.c @@ -10,6 +10,17 @@ #include "qosify.h" +static struct { + const char *suffix; + uint32_t flags; + int fd; +} bpf_progs[] = { + { "egress_eth", 0 }, + { "egress_ip", QOSIFY_IP_ONLY }, + { "ingress_eth", QOSIFY_INGRESS }, + { "ingress_ip", QOSIFY_INGRESS | QOSIFY_IP_ONLY }, +}; + static int qosify_bpf_pr(enum libbpf_print_level level, const char *format, va_list args) { @@ -38,8 +49,24 @@ static void qosify_fill_rodata(struct bpf_object *obj, uint32_t flags) } } +const char *qosify_get_program(uint32_t flags, int *fd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bpf_progs); i++) { + if (bpf_progs[i].flags != flags) + continue; + + *fd = bpf_progs[i].fd; + return bpf_progs[i].suffix; + } + + return NULL; +} + + static int -qosify_create_program(const char *suffix, uint32_t flags) +qosify_create_program(int idx) { DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, .pin_root_path = CLASSIFY_DATA_PATH, @@ -49,7 +76,7 @@ qosify_create_program(const char *suffix, uint32_t flags) char path[256]; int err; - snprintf(path, sizeof(path), CLASSIFY_PIN_PATH "_" "%s", suffix); + snprintf(path, sizeof(path), CLASSIFY_PIN_PATH "_" "%s", bpf_progs[idx].suffix); obj = bpf_object__open_file(CLASSIFY_PROG_PATH, &opts); err = libbpf_get_error(obj); @@ -66,7 +93,7 @@ qosify_create_program(const char *suffix, uint32_t flags) bpf_program__set_type(prog, BPF_PROG_TYPE_SCHED_CLS); - qosify_fill_rodata(obj, flags); + qosify_fill_rodata(obj, bpf_progs[idx].flags); err = bpf_object__load(obj); if (err) { @@ -81,24 +108,23 @@ qosify_create_program(const char *suffix, uint32_t flags) if (err) { fprintf(stderr, "Failed to pin program to %s: %s\n", path, strerror(-err)); + return -1; } bpf_object__close(obj); + err = bpf_obj_get(path); + if (err < 0) { + fprintf(stderr, "Failed to load pinned program %s: %s\n", + path, strerror(errno)); + } + bpf_progs[idx].fd = err; + return 0; } int qosify_loader_init(void) { - static const struct { - const char *suffix; - uint32_t flags; - } progs[] = { - { "egress_eth", 0 }, - { "egress_ip", QOSIFY_IP_ONLY }, - { "ingress_eth", QOSIFY_INGRESS }, - { "ingress_ip", QOSIFY_INGRESS | QOSIFY_IP_ONLY }, - }; glob_t g; int i; @@ -107,13 +133,12 @@ int qosify_loader_init(void) unlink(g.gl_pathv[i]); } - libbpf_set_print(qosify_bpf_pr); qosify_init_env(); - for (i = 0; i < ARRAY_SIZE(progs); i++) { - if (qosify_create_program(progs[i].suffix, progs[i].flags)) + for (i = 0; i < ARRAY_SIZE(bpf_progs); i++) { + if (qosify_create_program(i)) return -1; } diff --git a/qosify.h b/qosify.h index 5f14d4a..dab1ebb 100644 --- a/qosify.h +++ b/qosify.h @@ -78,6 +78,7 @@ extern struct qosify_flow_config flow_config; int qosify_run_cmd(char *cmd, bool ignore_error); int qosify_loader_init(void); +const char *qosify_get_program(uint32_t flags, int *fd); int qosify_map_init(void); int qosify_map_dscp_value(const char *val, uint8_t *dscp);