mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-30 01:52:51 +00:00
qosify: add new QoS package
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
54
feeds/ucentral/qosify/Makefile
Normal file
54
feeds/ucentral/qosify/Makefile
Normal file
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Copyright (C) 2008-2012 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=qosify
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
PKG_BUILD_DEPENDS:=bpf-headers
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
#include $(INCLUDE_DIR)/bpf.mk
|
||||
|
||||
define Package/qosify
|
||||
SECTION:=kernel
|
||||
CATEGORY:=Kernel modules
|
||||
SUBMENU:=Network Support
|
||||
TITLE:=QoS classifier eBPF module
|
||||
DEPENDS:=+libbpf +libubox +libubus +kmod-sched-cake +tc-full
|
||||
PKGFLAGS+=nonshared
|
||||
endef
|
||||
|
||||
#BPF_DOC = $(wildcard $(patsubst %,$(BPF_HEADERS_DIR)/scripts/%.py,bpf_doc bpf_helpers_doc))
|
||||
|
||||
TARGET_CFLAGS += -I$(BPF_HEADERS_DIR)/user_headers/include
|
||||
|
||||
define Build/Compile
|
||||
# $(call CompileBPF,$(PKG_BUILD_DIR)/qosify-bpf.c)
|
||||
$(Build/Compile/Default)
|
||||
endef
|
||||
|
||||
define Package/qosify/conffiles
|
||||
/etc/config/qosify
|
||||
/etc/qosify-defaults.conf
|
||||
endef
|
||||
|
||||
define Package/qosify/install
|
||||
$(INSTALL_DIR) $(1)/lib/bpf $(1)/usr/sbin $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/net
|
||||
$(INSTALL_DATA) ./files/qosify-bpf.o $(1)/lib/bpf
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/qosify $(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/qosify.init $(1)/etc/init.d/qosify
|
||||
$(INSTALL_DATA) ./files/qosify-defaults.conf $(1)/etc/qosify-defaults.conf
|
||||
$(INSTALL_DATA) ./files/qosify.conf $(1)/etc/config/qosify
|
||||
$(INSTALL_DATA) ./files/qosify.hotplug $(1)/etc/hotplug.d/net/10-qosify
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,qosify))
|
||||
BIN
feeds/ucentral/qosify/files/qosify-bpf.o
Normal file
BIN
feeds/ucentral/qosify/files/qosify-bpf.o
Normal file
Binary file not shown.
17
feeds/ucentral/qosify/files/qosify-defaults.conf
Normal file
17
feeds/ucentral/qosify/files/qosify-defaults.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
# DNS
|
||||
tcp:53 CS5
|
||||
tcp:5353 CS5
|
||||
udp:53 CS5
|
||||
udp:5353 CS5
|
||||
|
||||
# NTP
|
||||
udp:123 CS6
|
||||
|
||||
# SSH
|
||||
tcp:22 +CS4
|
||||
|
||||
# HTTP/QUIC
|
||||
tcp:80 +CS3
|
||||
tcp:443 +CS3
|
||||
udp:80 +CS3
|
||||
udp:443 +CS3
|
||||
28
feeds/ucentral/qosify/files/qosify.conf
Normal file
28
feeds/ucentral/qosify/files/qosify.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
config defaults
|
||||
list defaults /etc/qosify-defaults.conf
|
||||
option dscp_prio CS5
|
||||
option dscp_bulk CS0
|
||||
option dscp_default_udp CS4
|
||||
option bulk_trigger_timeout 5
|
||||
option bulk_trigger_pps 100
|
||||
|
||||
config interface wan
|
||||
option name wan
|
||||
option disabled 1
|
||||
option bandwidth_up 100mbit
|
||||
option bandwidth_down 100mbit
|
||||
# defaults:
|
||||
option ingress 1
|
||||
option egress 1
|
||||
option mode diffserv4
|
||||
option host_isolate 1
|
||||
option autorate_ingress 1
|
||||
option ingress_options ""
|
||||
option egress_options ""
|
||||
option options ""
|
||||
|
||||
config device wandev
|
||||
option disabled 1
|
||||
option name wan
|
||||
option bandwidth 100mbit
|
||||
|
||||
2
feeds/ucentral/qosify/files/qosify.hotplug
Normal file
2
feeds/ucentral/qosify/files/qosify.hotplug
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ubus call qosify check_devices
|
||||
103
feeds/ucentral/qosify/files/qosify.init
Normal file
103
feeds/ucentral/qosify/files/qosify.init
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2014 OpenWrt.org
|
||||
|
||||
START=19
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/qosify
|
||||
|
||||
add_option() {
|
||||
local type="$1"
|
||||
local name="$2"
|
||||
|
||||
config_get val "$cfg" "$name"
|
||||
|
||||
[ -n "$val" ] && json_add_$type "$name" "$val"
|
||||
}
|
||||
|
||||
add_defaults() {
|
||||
cfg="$1"
|
||||
|
||||
json_add_boolean reset 1
|
||||
|
||||
config_get files "$cfg" files
|
||||
json_add_array files
|
||||
for i in $files; do
|
||||
json_add_string "" "$i"
|
||||
done
|
||||
json_close_array
|
||||
|
||||
add_option int timeout
|
||||
add_option string dscp_prio
|
||||
add_option string dscp_bulk
|
||||
add_option string dscp_default_udp
|
||||
add_option string dscp_default_tcp
|
||||
add_option int bulk_trigger_timeout
|
||||
add_option int bulk_trigger_pps
|
||||
}
|
||||
|
||||
add_interface() {
|
||||
local cfg="$1"
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return
|
||||
|
||||
config_get name "$cfg" name
|
||||
json_add_object "$name"
|
||||
|
||||
config_get bw "$cfg" bandwidth
|
||||
|
||||
config_get bw_up "$cfg" bandwidth_up
|
||||
bw_up="${bw_up:-$bw}"
|
||||
[ -n "$bw_up" ] && json_add_string bandwidth_up "$bw_up"
|
||||
|
||||
config_get bw_down "$cfg" bandwidth_down
|
||||
bw_down="${bw_down:-$bw}"
|
||||
[ -n "$bw_down" ] && json_add_string bandwidth_down "$bw_down"
|
||||
|
||||
add_option string bandwidth
|
||||
add_option boolean ingress
|
||||
add_option boolean egress
|
||||
add_option string mode
|
||||
add_option boolean host_isolate
|
||||
add_option boolean autorate_ingress
|
||||
add_option string ingress_options
|
||||
add_option string egress_options
|
||||
add_option string options
|
||||
|
||||
json_close_object
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
|
||||
config_load qosify
|
||||
|
||||
config_foreach add_defaults defaults
|
||||
|
||||
json_add_object interfaces
|
||||
config_foreach add_interface interface
|
||||
json_close_object
|
||||
|
||||
json_add_object devices
|
||||
config_foreach add_interface device
|
||||
json_close_object
|
||||
|
||||
ubus call qosify config "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger qosify
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for qosify
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
15
feeds/ucentral/qosify/src/CMakeLists.txt
Normal file
15
feeds/ucentral/qosify/src/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
PROJECT(qosify C)
|
||||
|
||||
ADD_DEFINITIONS(-Os -Wall -Wno-unknown-warning-option -Wno-array-bounds -Wno-format-truncation -Werror --std=gnu99)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
find_library(bpf NAMES bpf)
|
||||
ADD_EXECUTABLE(qosify main.c loader.c map.c ubus.c interface.c)
|
||||
TARGET_LINK_LIBRARIES(qosify ${bpf} ubox ubus)
|
||||
|
||||
INSTALL(TARGETS qosify
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
557
feeds/ucentral/qosify/src/interface.c
Normal file
557
feeds/ucentral/qosify/src/interface.c
Normal file
@@ -0,0 +1,557 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libubox/vlist.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "qosify.h"
|
||||
|
||||
static void interface_update_cb(struct vlist_tree *tree,
|
||||
struct vlist_node *node_new,
|
||||
struct vlist_node *node_old);
|
||||
|
||||
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;
|
||||
|
||||
#define APPEND(_buf, _ofs, _format, ...) _ofs += snprintf(_buf + _ofs, sizeof(_buf) - _ofs, _format, ##__VA_ARGS__)
|
||||
|
||||
struct qosify_iface_config {
|
||||
struct blob_attr *data;
|
||||
|
||||
bool ingress;
|
||||
bool egress;
|
||||
bool nat;
|
||||
bool host_isolate;
|
||||
bool autorate_ingress;
|
||||
|
||||
const char *bandwidth_up;
|
||||
const char *bandwidth_down;
|
||||
const char *mode;
|
||||
const char *common_opts;
|
||||
const char *ingress_opts;
|
||||
const char *egress_opts;
|
||||
};
|
||||
|
||||
|
||||
struct qosify_iface {
|
||||
struct vlist_node node;
|
||||
|
||||
char ifname[IFNAMSIZ];
|
||||
bool active;
|
||||
|
||||
bool device;
|
||||
struct blob_attr *config_data;
|
||||
struct qosify_iface_config config;
|
||||
};
|
||||
|
||||
enum {
|
||||
IFACE_ATTR_BW_UP,
|
||||
IFACE_ATTR_BW_DOWN,
|
||||
IFACE_ATTR_INGRESS,
|
||||
IFACE_ATTR_EGRESS,
|
||||
IFACE_ATTR_MODE,
|
||||
IFACE_ATTR_NAT,
|
||||
IFACE_ATTR_HOST_ISOLATE,
|
||||
IFACE_ATTR_AUTORATE_IN,
|
||||
IFACE_ATTR_INGRESS_OPTS,
|
||||
IFACE_ATTR_EGRESS_OPTS,
|
||||
IFACE_ATTR_OPTS,
|
||||
__IFACE_ATTR_MAX
|
||||
};
|
||||
|
||||
static inline const char *qosify_iface_name(struct qosify_iface *iface)
|
||||
{
|
||||
return iface->node.avl.key;
|
||||
}
|
||||
|
||||
static void
|
||||
iface_config_parse(struct blob_attr *attr, struct blob_attr **tb)
|
||||
{
|
||||
static const struct blobmsg_policy policy[__IFACE_ATTR_MAX] = {
|
||||
[IFACE_ATTR_BW_UP] = { "bandwidth_up", BLOBMSG_TYPE_STRING },
|
||||
[IFACE_ATTR_BW_DOWN] = { "bandwidth_down", BLOBMSG_TYPE_STRING },
|
||||
[IFACE_ATTR_INGRESS] = { "ingress", BLOBMSG_TYPE_BOOL },
|
||||
[IFACE_ATTR_EGRESS] = { "egress", BLOBMSG_TYPE_BOOL },
|
||||
[IFACE_ATTR_MODE] = { "mode", BLOBMSG_TYPE_STRING },
|
||||
[IFACE_ATTR_NAT] = { "nat", BLOBMSG_TYPE_BOOL },
|
||||
[IFACE_ATTR_HOST_ISOLATE] = { "host_isolate", BLOBMSG_TYPE_BOOL },
|
||||
[IFACE_ATTR_AUTORATE_IN] = { "autorate_ingress", BLOBMSG_TYPE_BOOL },
|
||||
[IFACE_ATTR_INGRESS_OPTS] = { "ingress_options", BLOBMSG_TYPE_STRING },
|
||||
[IFACE_ATTR_EGRESS_OPTS] = { "egress_options", BLOBMSG_TYPE_STRING },
|
||||
[IFACE_ATTR_OPTS] = { "options", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
blobmsg_parse(policy, __IFACE_ATTR_MAX, tb, blobmsg_data(attr), blobmsg_len(attr));
|
||||
}
|
||||
|
||||
static bool
|
||||
iface_config_equal(struct qosify_iface *if1, struct qosify_iface *if2)
|
||||
{
|
||||
struct blob_attr *tb1[__IFACE_ATTR_MAX], *tb2[__IFACE_ATTR_MAX];
|
||||
int i;
|
||||
|
||||
iface_config_parse(if1->config_data, tb1);
|
||||
iface_config_parse(if2->config_data, tb2);
|
||||
|
||||
for (i = 0; i < __IFACE_ATTR_MAX; i++) {
|
||||
if (!!tb1[i] != !!tb2[i])
|
||||
return false;
|
||||
|
||||
if (!tb1[i])
|
||||
continue;
|
||||
|
||||
if (blob_raw_len(tb1[i]) != blob_raw_len(tb2[i]))
|
||||
return false;
|
||||
|
||||
if (memcmp(tb1[i], tb2[i], blob_raw_len(tb1[i])) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *check_str(struct blob_attr *attr)
|
||||
{
|
||||
const char *str = blobmsg_get_string(attr);
|
||||
|
||||
if (strchr(str, '\''))
|
||||
return NULL;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
iface_config_set(struct qosify_iface_config *cfg, struct blob_attr *attr)
|
||||
{
|
||||
struct blob_attr *tb[__IFACE_ATTR_MAX];
|
||||
struct blob_attr *cur;
|
||||
|
||||
iface_config_parse(attr, tb);
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
/* defaults */
|
||||
cfg->mode = "diffserv4";
|
||||
cfg->ingress = true;
|
||||
cfg->egress = true;
|
||||
cfg->host_isolate = true;
|
||||
cfg->autorate_ingress = true;
|
||||
|
||||
if ((cur = tb[IFACE_ATTR_BW_UP]) != NULL)
|
||||
cfg->bandwidth_up = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_BW_DOWN]) != NULL)
|
||||
cfg->bandwidth_down = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_MODE]) != NULL)
|
||||
cfg->mode = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_OPTS]) != NULL)
|
||||
cfg->common_opts = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_EGRESS_OPTS]) != NULL)
|
||||
cfg->egress_opts = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_INGRESS_OPTS]) != NULL)
|
||||
cfg->ingress_opts = check_str(cur);
|
||||
if ((cur = tb[IFACE_ATTR_INGRESS]) != NULL)
|
||||
cfg->ingress = blobmsg_get_bool(cur);
|
||||
if ((cur = tb[IFACE_ATTR_EGRESS]) != NULL)
|
||||
cfg->egress = blobmsg_get_bool(cur);
|
||||
if ((cur = tb[IFACE_ATTR_NAT]) != NULL)
|
||||
cfg->nat = blobmsg_get_bool(cur);
|
||||
if ((cur = tb[IFACE_ATTR_HOST_ISOLATE]) != NULL)
|
||||
cfg->host_isolate = blobmsg_get_bool(cur);
|
||||
if ((cur = tb[IFACE_ATTR_AUTORATE_IN]) != NULL)
|
||||
cfg->autorate_ingress = blobmsg_get_bool(cur);
|
||||
}
|
||||
|
||||
static const char *
|
||||
interface_ifb_name(struct qosify_iface *iface)
|
||||
{
|
||||
static char ifname[IFNAMSIZ + 1] = "ifb-";
|
||||
int len = strlen(iface->ifname);
|
||||
|
||||
if (len + 4 < IFNAMSIZ) {
|
||||
snprintf(ifname + 4, IFNAMSIZ - 4, "%s", iface->ifname);
|
||||
|
||||
return ifname;
|
||||
}
|
||||
|
||||
ifname[4] = iface->ifname[0];
|
||||
ifname[5] = iface->ifname[1];
|
||||
snprintf(ifname + 6, IFNAMSIZ - 6, "%s", iface->ifname + len - (IFNAMSIZ + 6) - 1);
|
||||
|
||||
return ifname;
|
||||
}
|
||||
|
||||
static int run_cmd(char *cmd, bool ignore)
|
||||
{
|
||||
char *argv[] = { "sh", "-c", cmd, NULL };
|
||||
bool first = true;
|
||||
int status = -1;
|
||||
char buf[512];
|
||||
int fds[2];
|
||||
FILE *f;
|
||||
int pid;
|
||||
|
||||
if (pipe(fds))
|
||||
return -1;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
close(fds[0]);
|
||||
if (fds[1] != STDOUT_FILENO)
|
||||
dup2(fds[1], STDOUT_FILENO);
|
||||
if (fds[1] != STDERR_FILENO)
|
||||
dup2(fds[1], STDERR_FILENO);
|
||||
if (fds[1] > STDERR_FILENO)
|
||||
close(fds[1]);
|
||||
execv("/bin/sh", argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
|
||||
close(fds[1]);
|
||||
f = fdopen(fds[0], "r");
|
||||
if (!f) {
|
||||
close(fds[0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), f) != NULL) {
|
||||
if (!strlen(buf))
|
||||
break;
|
||||
if (ignore)
|
||||
continue;
|
||||
if (first) {
|
||||
ULOG_WARN("Command: %s\n", cmd);
|
||||
first = false;
|
||||
}
|
||||
ULOG_WARN("%s%s", buf, strchr(buf, '\n') ? "" : "\n");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
out:
|
||||
while (waitpid(pid, &status, 0) < 0)
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
prepare_tc_cmd(char *buf, int len, const char *type, const char *cmd,
|
||||
const char *dev, const char *extra)
|
||||
{
|
||||
return snprintf(buf, len, "tc %s %s dev '%s' %s", type, cmd, dev, extra);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_del_qdisc(const char *ifname, const char *type)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
prepare_tc_cmd(buf, sizeof(buf), "qdisc", "del", ifname, type);
|
||||
|
||||
return run_cmd(buf, true);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_add_qdisc(struct qosify_iface *iface, const char *ifname, bool egress, bool eth)
|
||||
{
|
||||
struct qosify_iface_config *cfg = &iface->config;
|
||||
const char *bw = egress ? cfg->bandwidth_up : cfg->bandwidth_down;
|
||||
const char *dir_opts = egress ? cfg->egress_opts : cfg->ingress_opts;
|
||||
char buf[512];
|
||||
int ofs;
|
||||
|
||||
cmd_del_qdisc(ifname, "root");
|
||||
|
||||
ofs = prepare_tc_cmd(buf, sizeof(buf), "qdisc", "add", ifname, "root handle 1: cake");
|
||||
if (bw)
|
||||
APPEND(buf, ofs, " bandwidth %s", bw);
|
||||
|
||||
APPEND(buf, ofs, " %s %sgress", cfg->mode, egress ? "e" : "in");
|
||||
|
||||
if (cfg->host_isolate)
|
||||
APPEND(buf, ofs, " %snat dual-%shost",
|
||||
cfg->nat ? "" : "no",
|
||||
egress ? "src" : "dst");
|
||||
else
|
||||
APPEND(buf, ofs, " flows");
|
||||
|
||||
APPEND(buf, ofs, " %s %s",
|
||||
cfg->common_opts ? cfg->common_opts : "",
|
||||
dir_opts ? dir_opts : "");
|
||||
|
||||
run_cmd(buf, false);
|
||||
|
||||
ofs = prepare_tc_cmd(buf, sizeof(buf), "filter", "add", ifname, "parent 1: bpf");
|
||||
APPEND(buf, ofs, " object-pinned /sys/fs/bpf/qosify_%sgress_%s verbose direct-action",
|
||||
egress ? "e" : "in",
|
||||
eth ? "eth" : "ip");
|
||||
|
||||
return run_cmd(buf, false);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_del_ingress(struct qosify_iface *iface)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
cmd_del_qdisc(iface->ifname, "handle ffff: ingress");
|
||||
snprintf(buf, sizeof(buf), "ip link del '%s'", interface_ifb_name(iface));
|
||||
|
||||
return run_cmd(buf, true);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
cmd_add_ingress(struct qosify_iface *iface, bool eth)
|
||||
{
|
||||
const char *ifbdev = interface_ifb_name(iface);
|
||||
char buf[256];
|
||||
int ofs;
|
||||
|
||||
cmd_del_ingress(iface);
|
||||
|
||||
ofs = prepare_tc_cmd(buf, sizeof(buf), "qdisc", "add", iface->ifname, " handle ffff: ingress");
|
||||
run_cmd(buf, false);
|
||||
|
||||
snprintf(buf, sizeof(buf), "ip link add '%s' type ifb", ifbdev);
|
||||
run_cmd(buf, false);
|
||||
|
||||
cmd_add_qdisc(iface, ifbdev, false, eth);
|
||||
|
||||
snprintf(buf, sizeof(buf), "ip link set dev '%s' up", ifbdev);
|
||||
run_cmd(buf, false);
|
||||
|
||||
ofs = prepare_tc_cmd(buf, sizeof(buf), "filter", "add", iface->ifname, " parent ffff:");
|
||||
APPEND(buf, ofs, " protocol all prio 10 u32 match u32 0 0 "
|
||||
"flowid 1:1 action mirred egress redirect dev '%s'", ifbdev);
|
||||
return run_cmd(buf, false);
|
||||
}
|
||||
|
||||
static void
|
||||
interface_start(struct qosify_iface *iface)
|
||||
{
|
||||
struct ifreq ifr = {};
|
||||
bool eth;
|
||||
|
||||
if (!iface->ifname[0] || iface->active)
|
||||
return;
|
||||
|
||||
ULOG_INFO("start interface %s\n", iface->ifname);
|
||||
|
||||
strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
|
||||
if (ioctl(socket_fd, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
ULOG_ERR("ioctl(SIOCGIFHWADDR, %s) failed: %s\n", iface->ifname, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
eth = ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER;
|
||||
|
||||
if (iface->config.egress)
|
||||
cmd_add_qdisc(iface, iface->ifname, true, eth);
|
||||
if (iface->config.ingress)
|
||||
cmd_add_ingress(iface, eth);
|
||||
|
||||
iface->active = true;
|
||||
}
|
||||
|
||||
static void
|
||||
interface_stop(struct qosify_iface *iface)
|
||||
{
|
||||
if (!iface->ifname[0] || !iface->active)
|
||||
return;
|
||||
|
||||
ULOG_INFO("stop interface %s\n", iface->ifname);
|
||||
iface->active = false;
|
||||
|
||||
if (iface->config.egress)
|
||||
cmd_del_qdisc(iface->ifname, "root");
|
||||
if (iface->config.ingress)
|
||||
cmd_del_ingress(iface);
|
||||
}
|
||||
|
||||
static void
|
||||
interface_set_config(struct qosify_iface *iface, struct blob_attr *config)
|
||||
{
|
||||
iface->config_data = blob_memdup(config);
|
||||
iface_config_set(&iface->config, iface->config_data);
|
||||
interface_start(iface);
|
||||
}
|
||||
|
||||
static void
|
||||
interface_update_cb(struct vlist_tree *tree,
|
||||
struct vlist_node *node_new, struct vlist_node *node_old)
|
||||
{
|
||||
struct qosify_iface *if_new = NULL, *if_old = NULL;
|
||||
|
||||
if (node_new)
|
||||
if_new = container_of(node_new, struct qosify_iface, node);
|
||||
if (node_old)
|
||||
if_old = container_of(node_old, struct qosify_iface, node);
|
||||
|
||||
if (if_new && if_old) {
|
||||
if (!iface_config_equal(if_old, if_new)) {
|
||||
interface_stop(if_old);
|
||||
free(if_old->config_data);
|
||||
interface_set_config(if_old, if_new->config_data);
|
||||
}
|
||||
|
||||
free(if_new);
|
||||
return;
|
||||
}
|
||||
|
||||
if (if_old) {
|
||||
interface_stop(if_old);
|
||||
free(if_old->config_data);
|
||||
free(if_old);
|
||||
}
|
||||
|
||||
if (if_new)
|
||||
interface_set_config(if_new, if_new->config_data);
|
||||
}
|
||||
|
||||
static void
|
||||
interface_create(struct blob_attr *attr, bool device)
|
||||
{
|
||||
struct qosify_iface *iface;
|
||||
const char *name = blobmsg_name(attr);
|
||||
int name_len = strlen(name);
|
||||
char *name_buf;
|
||||
|
||||
if (strchr(name, '\''))
|
||||
return;
|
||||
|
||||
if (name_len >= IFNAMSIZ)
|
||||
return;
|
||||
|
||||
if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
|
||||
return;
|
||||
|
||||
iface = calloc_a(sizeof(*iface), &name_buf, name_len + 1);
|
||||
strcpy(name_buf, blobmsg_name(attr));
|
||||
iface->config_data = attr;
|
||||
iface->device = device;
|
||||
vlist_add(device ? &devices : &interfaces, &iface->node, name_buf);
|
||||
}
|
||||
|
||||
void qosify_iface_config_update(struct blob_attr *ifaces, struct blob_attr *devs)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
vlist_update(&devices);
|
||||
blobmsg_for_each_attr(cur, devs, rem)
|
||||
interface_create(cur, true);
|
||||
vlist_flush(&devices);
|
||||
|
||||
vlist_update(&interfaces);
|
||||
blobmsg_for_each_attr(cur, ifaces, rem)
|
||||
interface_create(cur, false);
|
||||
vlist_flush(&interfaces);
|
||||
}
|
||||
|
||||
static void
|
||||
qosify_iface_check_device(struct qosify_iface *iface)
|
||||
{
|
||||
const char *name = qosify_iface_name(iface);
|
||||
int ifindex;
|
||||
|
||||
ifindex = if_nametoindex(name);
|
||||
if (!ifindex) {
|
||||
interface_stop(iface);
|
||||
iface->ifname[0] = 0;
|
||||
} else {
|
||||
snprintf(iface->ifname, sizeof(iface->ifname), "%s", name);
|
||||
interface_start(iface);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qosify_iface_check_interface(struct qosify_iface *iface)
|
||||
{
|
||||
const char *name = qosify_iface_name(iface);
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (qosify_ubus_check_interface(name, ifname, sizeof(ifname)) == 0) {
|
||||
snprintf(iface->ifname, sizeof(iface->ifname), "%s", ifname);
|
||||
interface_start(iface);
|
||||
} else {
|
||||
interface_stop(iface);
|
||||
iface->ifname[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void qos_iface_check_cb(struct uloop_timeout *t)
|
||||
{
|
||||
struct qosify_iface *iface;
|
||||
|
||||
vlist_for_each_element(&devices, iface, node)
|
||||
qosify_iface_check_device(iface);
|
||||
vlist_for_each_element(&interfaces, iface, node)
|
||||
qosify_iface_check_interface(iface);
|
||||
}
|
||||
|
||||
void qosify_iface_check(void)
|
||||
{
|
||||
static struct uloop_timeout timer = {
|
||||
.cb = qos_iface_check_cb,
|
||||
};
|
||||
|
||||
uloop_timeout_set(&timer, 10);
|
||||
}
|
||||
|
||||
void qosify_iface_status(struct blob_buf *b)
|
||||
{
|
||||
struct qosify_iface *iface;
|
||||
void *c, *i;
|
||||
|
||||
c = blobmsg_open_table(b, "devices");
|
||||
vlist_for_each_element(&devices, iface, node) {
|
||||
i = blobmsg_open_table(b, qosify_iface_name(iface));
|
||||
blobmsg_add_u8(b, "active", iface->active);
|
||||
blobmsg_close_table(b, i);
|
||||
}
|
||||
blobmsg_close_table(b, c);
|
||||
|
||||
c = blobmsg_open_table(b, "interfaces");
|
||||
vlist_for_each_element(&interfaces, iface, node) {
|
||||
i = blobmsg_open_table(b, qosify_iface_name(iface));
|
||||
blobmsg_add_u8(b, "active", iface->active);
|
||||
if (iface->ifname)
|
||||
blobmsg_add_string(b, "ifname", iface->ifname);
|
||||
blobmsg_close_table(b, i);
|
||||
}
|
||||
blobmsg_close_table(b, c);
|
||||
}
|
||||
|
||||
int qosify_iface_init(void)
|
||||
{
|
||||
socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (socket < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qosify_iface_stop(void)
|
||||
{
|
||||
struct qosify_iface *iface;
|
||||
|
||||
vlist_for_each_element(&interfaces, iface, node)
|
||||
interface_stop(iface);
|
||||
vlist_for_each_element(&devices, iface, node)
|
||||
interface_stop(iface);
|
||||
}
|
||||
|
||||
126
feeds/ucentral/qosify/src/loader.c
Normal file
126
feeds/ucentral/qosify/src/loader.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <glob.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qosify.h"
|
||||
|
||||
static int qosify_bpf_pr(enum libbpf_print_level level, const char *format,
|
||||
va_list args)
|
||||
{
|
||||
return vfprintf(stderr, format, args);
|
||||
}
|
||||
|
||||
static void qosify_init_env(void)
|
||||
{
|
||||
struct rlimit limit = {
|
||||
.rlim_cur = RLIM_INFINITY,
|
||||
.rlim_max = RLIM_INFINITY,
|
||||
};
|
||||
|
||||
setrlimit(RLIMIT_MEMLOCK, &limit);
|
||||
}
|
||||
|
||||
static void qosify_fill_rodata(struct bpf_object *obj, uint32_t flags)
|
||||
{
|
||||
struct bpf_map *map = NULL;
|
||||
|
||||
while ((map = bpf_map__next(map, obj)) != NULL) {
|
||||
if (!strstr(bpf_map__name(map), ".rodata"))
|
||||
continue;
|
||||
|
||||
bpf_map__set_initial_value(map, &flags, sizeof(flags));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_create_program(const char *suffix, uint32_t flags, bool *force_init)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
|
||||
.pin_root_path = CLASSIFY_DATA_PATH,
|
||||
);
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
struct stat st;
|
||||
char path[256];
|
||||
int err;
|
||||
|
||||
snprintf(path, sizeof(path), CLASSIFY_PIN_PATH "_" "%s", suffix);
|
||||
if (!*force_init) {
|
||||
if (stat(path, &st) == 0)
|
||||
return 0;
|
||||
|
||||
*force_init = true;
|
||||
}
|
||||
|
||||
obj = bpf_object__open_file(CLASSIFY_PROG_PATH, &opts);
|
||||
err = libbpf_get_error(obj);
|
||||
if (err) {
|
||||
perror("bpf_object__open_file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, "classifier");
|
||||
if (!prog) {
|
||||
fprintf(stderr, "Can't find classifier prog\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bpf_program__set_type(prog, BPF_PROG_TYPE_SCHED_CLS);
|
||||
|
||||
qosify_fill_rodata(obj, flags);
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (err) {
|
||||
perror("bpf_object__load");
|
||||
return -1;
|
||||
}
|
||||
|
||||
libbpf_set_print(NULL);
|
||||
|
||||
unlink(path);
|
||||
err = bpf_program__pin(prog, path);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to pin program to %s: %s\n",
|
||||
path, strerror(-err));
|
||||
}
|
||||
|
||||
bpf_object__close(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qosify_loader_init(bool force_init)
|
||||
{
|
||||
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;
|
||||
|
||||
if (force_init &&
|
||||
glob(CLASSIFY_DATA_PATH "/*", 0, NULL, &g) == 0) {
|
||||
for (i = 0; i < g.gl_pathc; i++)
|
||||
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,
|
||||
&force_init))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
70
feeds/ucentral/qosify/src/main.c
Normal file
70
feeds/ucentral/qosify/src/main.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "qosify.h"
|
||||
|
||||
static int usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [options]\n"
|
||||
"Options:\n"
|
||||
" -f: force reload of BPF programs\n"
|
||||
" -l <file> Load defaults from <file>\n"
|
||||
" -o only load program/maps without running as daemon\n"
|
||||
"\n", progname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *load_file = NULL;
|
||||
bool force_init = false;
|
||||
bool oneshot = false;
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "fl:o")) != -1) {
|
||||
switch (ch) {
|
||||
case 'f':
|
||||
force_init = true;
|
||||
break;
|
||||
case 'l':
|
||||
load_file = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
oneshot = true;
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (qosify_loader_init(force_init))
|
||||
return 2;
|
||||
|
||||
if (qosify_map_init())
|
||||
return 2;
|
||||
|
||||
if (qosify_map_load_file(load_file))
|
||||
return 2;
|
||||
|
||||
if (oneshot)
|
||||
return 0;
|
||||
|
||||
ulog_open(ULOG_SYSLOG, LOG_DAEMON, "qosify");
|
||||
uloop_init();
|
||||
|
||||
if (qosify_ubus_init() ||
|
||||
qosify_iface_init())
|
||||
return 2;
|
||||
|
||||
uloop_run();
|
||||
|
||||
qosify_iface_stop();
|
||||
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
575
feeds/ucentral/qosify/src/map.c
Normal file
575
feeds/ucentral/qosify/src/map.c
Normal file
@@ -0,0 +1,575 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "qosify.h"
|
||||
|
||||
static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr);
|
||||
|
||||
static int qosify_map_fds[__CL_MAP_MAX];
|
||||
static AVL_TREE(map_data, qosify_map_entry_cmp, false, NULL);
|
||||
static LIST_HEAD(map_files);
|
||||
static uint32_t next_timeout;
|
||||
static uint8_t qosify_dscp_default[2] = { 0xff, 0xff };
|
||||
int qosify_map_timeout = 3600;
|
||||
struct qosify_config config;
|
||||
|
||||
struct qosify_map_file {
|
||||
struct list_head list;
|
||||
char filename[];
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *type_name;
|
||||
} qosify_map_info[] = {
|
||||
[CL_MAP_TCP_PORTS] = { "tcp_ports", "tcp_port" },
|
||||
[CL_MAP_UDP_PORTS] = { "udp_ports", "udp_port" },
|
||||
[CL_MAP_IPV4_ADDR] = { "ipv4_map", "ipv4_addr" },
|
||||
[CL_MAP_IPV6_ADDR] = { "ipv6_map", "ipv6_addr" },
|
||||
[CL_MAP_CONFIG] = { "config", "config" },
|
||||
};
|
||||
|
||||
static void qosify_map_timer_cb(struct uloop_timeout *t)
|
||||
{
|
||||
qosify_map_gc();
|
||||
}
|
||||
|
||||
static struct uloop_timeout qosify_map_timer = {
|
||||
.cb = qosify_map_timer_cb,
|
||||
};
|
||||
|
||||
static uint32_t qosify_gettime(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return ts.tv_sec;
|
||||
}
|
||||
|
||||
static const char *
|
||||
qosify_map_path(enum qosify_map_id id)
|
||||
{
|
||||
static char path[128];
|
||||
const char *name;
|
||||
|
||||
if (id >= ARRAY_SIZE(qosify_map_info))
|
||||
return NULL;
|
||||
|
||||
name = qosify_map_info[id].name;
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", CLASSIFY_DATA_PATH, name);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int qosify_map_get_fd(enum qosify_map_id id)
|
||||
{
|
||||
const char *path = qosify_map_path(id);
|
||||
int fd;
|
||||
|
||||
if (!path)
|
||||
return -1;
|
||||
|
||||
fd = bpf_obj_get(path);
|
||||
if (fd < 0)
|
||||
fprintf(stderr, "Failed to open map %s: %s\n", path, strerror(errno));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void qosify_map_clear_list(enum qosify_map_id id)
|
||||
{
|
||||
int fd = qosify_map_fds[id];
|
||||
__u32 key[4] = {};
|
||||
|
||||
while (bpf_map_get_next_key(fd, &key, &key) != -1)
|
||||
bpf_map_delete_elem(fd, &key);
|
||||
}
|
||||
|
||||
static void __qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
|
||||
{
|
||||
struct qosify_map_data data = {
|
||||
.id = id,
|
||||
};
|
||||
int fd = qosify_map_fds[id];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (1 << 16); i++) {
|
||||
data.addr.port = htons(i);
|
||||
if (avl_find(&map_data, &data))
|
||||
continue;
|
||||
|
||||
bpf_map_update_elem(fd, &data.addr, &val, BPF_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val)
|
||||
{
|
||||
bool udp;
|
||||
|
||||
if (id == CL_MAP_TCP_PORTS)
|
||||
udp = false;
|
||||
else if (id == CL_MAP_UDP_PORTS)
|
||||
udp = true;
|
||||
else
|
||||
return;
|
||||
|
||||
if (qosify_dscp_default[udp] == val)
|
||||
return;
|
||||
|
||||
qosify_dscp_default[udp] = val;
|
||||
__qosify_map_set_dscp_default(id, val);
|
||||
}
|
||||
|
||||
int qosify_map_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qosify_map_fds); i++) {
|
||||
qosify_map_fds[i] = qosify_map_get_fd(i);
|
||||
if (qosify_map_fds[i] < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
qosify_map_clear_list(CL_MAP_IPV4_ADDR);
|
||||
qosify_map_clear_list(CL_MAP_IPV6_ADDR);
|
||||
qosify_map_reset_config();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *str_skip(char *str, bool space)
|
||||
{
|
||||
while (*str && isspace(*str) == space)
|
||||
str++;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_map_codepoint(const char *val)
|
||||
{
|
||||
static const struct {
|
||||
const char name[5];
|
||||
uint8_t val;
|
||||
} cp[] = {
|
||||
{ "CS0", 0 },
|
||||
{ "CS1", 8 },
|
||||
{ "CS2", 16 },
|
||||
{ "CS3", 24 },
|
||||
{ "CS4", 32 },
|
||||
{ "CS5", 40 },
|
||||
{ "CS6", 48 },
|
||||
{ "CS7", 56 },
|
||||
{ "AF11", 10 },
|
||||
{ "AF12", 12 },
|
||||
{ "AF13", 14 },
|
||||
{ "AF21", 18 },
|
||||
{ "AF22", 20 },
|
||||
{ "AF22", 22 },
|
||||
{ "AF31", 26 },
|
||||
{ "AF32", 28 },
|
||||
{ "AF33", 30 },
|
||||
{ "AF41", 34 },
|
||||
{ "AF42", 36 },
|
||||
{ "AF43", 38 },
|
||||
{ "EF", 46 },
|
||||
{ "VA", 44 },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cp); i++)
|
||||
if (!strcmp(cp[i].name, val))
|
||||
return cp[i].val;
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static int qosify_map_entry_cmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
const struct qosify_map_data *d1 = k1;
|
||||
const struct qosify_map_data *d2 = k2;
|
||||
|
||||
if (d1->id != d2->id)
|
||||
return d2->id - d1->id;
|
||||
|
||||
return memcmp(&d1->addr, &d2->addr, sizeof(d1->addr));
|
||||
}
|
||||
|
||||
static void __qosify_map_set_entry(struct qosify_map_data *data)
|
||||
{
|
||||
int fd = qosify_map_fds[data->id];
|
||||
struct qosify_map_entry *e;
|
||||
bool file = data->file;
|
||||
int32_t delta = 0;
|
||||
bool add = data->dscp != 0xff;
|
||||
uint8_t prev_dscp = 0xff;
|
||||
|
||||
e = avl_find_element(&map_data, data, e, avl);
|
||||
if (!e) {
|
||||
if (!add)
|
||||
return;
|
||||
|
||||
e = calloc(1, sizeof(*e));
|
||||
e->avl.key = &e->data;
|
||||
e->data.id = data->id;
|
||||
memcpy(&e->data.addr, &data->addr, sizeof(e->data.addr));
|
||||
avl_insert(&map_data, &e->avl);
|
||||
} else {
|
||||
prev_dscp = e->data.dscp;
|
||||
}
|
||||
|
||||
if (file)
|
||||
e->data.file = add;
|
||||
else
|
||||
e->data.user = add;
|
||||
|
||||
if (add) {
|
||||
if (file)
|
||||
e->data.file_dscp = data->dscp;
|
||||
if (!e->data.user || !file)
|
||||
e->data.dscp = data->dscp;
|
||||
} else if (e->data.file && !file) {
|
||||
e->data.dscp = e->data.file_dscp;
|
||||
}
|
||||
|
||||
if (e->data.dscp != prev_dscp)
|
||||
bpf_map_update_elem(fd, &data->addr, &e->data.dscp, BPF_ANY);
|
||||
|
||||
if (add) {
|
||||
if (qosify_map_timeout == ~0 || file) {
|
||||
e->timeout = ~0;
|
||||
return;
|
||||
}
|
||||
|
||||
e->timeout = qosify_gettime() + qosify_map_timeout;
|
||||
delta = e->timeout - next_timeout;
|
||||
if (next_timeout && delta >= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
uloop_timeout_set(&qosify_map_timer, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_map_set_port(struct qosify_map_data *data, const char *str)
|
||||
{
|
||||
unsigned long start_port, end_port;
|
||||
char *err;
|
||||
int i;
|
||||
|
||||
start_port = end_port = strtoul(str, &err, 0);
|
||||
if (err && *err) {
|
||||
if (*err == '-')
|
||||
end_port = strtoul(err + 1, &err, 0);
|
||||
if (*err)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!start_port || end_port < start_port ||
|
||||
end_port >= 65535)
|
||||
return -1;
|
||||
|
||||
for (i = start_port; i <= end_port; i++) {
|
||||
data->addr.port = htons(i);
|
||||
__qosify_map_set_entry(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_map_fill_ip(struct qosify_map_data *data, const char *str)
|
||||
{
|
||||
int af;
|
||||
|
||||
if (data->id == CL_MAP_IPV6_ADDR)
|
||||
af = AF_INET6;
|
||||
else
|
||||
af = AF_INET;
|
||||
|
||||
if (inet_pton(af, str, &data->addr) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, uint8_t dscp)
|
||||
{
|
||||
struct qosify_map_data data = {
|
||||
.id = id,
|
||||
.file = file,
|
||||
.dscp = dscp,
|
||||
};
|
||||
|
||||
switch (id) {
|
||||
case CL_MAP_TCP_PORTS:
|
||||
case CL_MAP_UDP_PORTS:
|
||||
return qosify_map_set_port(&data, str);
|
||||
case CL_MAP_IPV4_ADDR:
|
||||
case CL_MAP_IPV6_ADDR:
|
||||
if (qosify_map_fill_ip(&data, str))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
__qosify_map_set_entry(&data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qosify_map_dscp_value(const char *val)
|
||||
{
|
||||
unsigned long dscp;
|
||||
char *err;
|
||||
bool fallback = false;
|
||||
|
||||
if (*val == '+') {
|
||||
fallback = true;
|
||||
val++;
|
||||
}
|
||||
|
||||
dscp = strtoul(val, &err, 0);
|
||||
if (err && *err)
|
||||
dscp = qosify_map_codepoint(val);
|
||||
|
||||
if (dscp >= 64)
|
||||
return -1;
|
||||
|
||||
return dscp + (fallback << 6);
|
||||
}
|
||||
|
||||
static void
|
||||
qosify_map_parse_line(char *str)
|
||||
{
|
||||
const char *key, *value;
|
||||
int dscp;
|
||||
|
||||
str = str_skip(str, true);
|
||||
key = str;
|
||||
|
||||
str = str_skip(str, false);
|
||||
if (!*str)
|
||||
return;
|
||||
|
||||
*(str++) = 0;
|
||||
str = str_skip(str, true);
|
||||
value = str;
|
||||
|
||||
dscp = qosify_map_dscp_value(value);
|
||||
if (dscp < 0)
|
||||
return;
|
||||
|
||||
if (!strncmp(key, "tcp:", 4))
|
||||
qosify_map_set_entry(CL_MAP_TCP_PORTS, true, key + 4, dscp);
|
||||
else if (!strncmp(key, "udp:", 4))
|
||||
qosify_map_set_entry(CL_MAP_UDP_PORTS, true, key + 4, dscp);
|
||||
else if (strchr(key, ':'))
|
||||
qosify_map_set_entry(CL_MAP_IPV6_ADDR, true, key, dscp);
|
||||
else if (strchr(key, '.'))
|
||||
qosify_map_set_entry(CL_MAP_IPV4_ADDR, true, key, dscp);
|
||||
}
|
||||
|
||||
static int __qosify_map_load_file(const char *file)
|
||||
{
|
||||
char line[1024];
|
||||
char *cur;
|
||||
FILE *f;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
f = fopen(file, "r");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Can't open data file %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
cur = strchr(line, '#');
|
||||
if (cur)
|
||||
*cur = 0;
|
||||
|
||||
cur = line + strlen(line);
|
||||
if (cur == line)
|
||||
continue;
|
||||
|
||||
while (cur > line && isspace(cur[-1]))
|
||||
cur--;
|
||||
|
||||
*cur = 0;
|
||||
qosify_map_parse_line(line);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qosify_map_load_file(const char *file)
|
||||
{
|
||||
struct qosify_map_file *f;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
f = calloc(1, sizeof(*f) + strlen(file) + 1);
|
||||
strcpy(f->filename, file);
|
||||
list_add_tail(&f->list, &map_files);
|
||||
|
||||
return __qosify_map_load_file(file);
|
||||
}
|
||||
|
||||
static void qosify_map_reset_file_entries(void)
|
||||
{
|
||||
struct qosify_map_entry *e;
|
||||
|
||||
avl_for_each_element(&map_data, e, avl)
|
||||
e->data.file = false;
|
||||
}
|
||||
|
||||
void qosify_map_clear_files(void)
|
||||
{
|
||||
struct qosify_map_file *f, *tmp;
|
||||
|
||||
qosify_map_reset_file_entries();
|
||||
|
||||
list_for_each_entry_safe(f, tmp, &map_files, list) {
|
||||
list_del(&f->list);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
void qosify_map_reset_config(void)
|
||||
{
|
||||
qosify_map_clear_files();
|
||||
qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, 0);
|
||||
qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, 0);
|
||||
qosify_map_timeout = 3600;
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.dscp_prio = 0xff;
|
||||
config.dscp_bulk = 0xff;
|
||||
config.dscp_icmp = 0xff;
|
||||
}
|
||||
|
||||
void qosify_map_reload(void)
|
||||
{
|
||||
struct qosify_map_file *f;
|
||||
|
||||
qosify_map_reset_file_entries();
|
||||
|
||||
list_for_each_entry(f, &map_files, list)
|
||||
__qosify_map_load_file(f->filename);
|
||||
|
||||
qosify_map_gc();
|
||||
}
|
||||
|
||||
void qosify_map_gc(void)
|
||||
{
|
||||
struct qosify_map_entry *e, *tmp;
|
||||
int32_t timeout = 0;
|
||||
uint32_t cur_time = qosify_gettime();
|
||||
int fd;
|
||||
|
||||
next_timeout = 0;
|
||||
avl_for_each_element_safe(&map_data, e, avl, tmp) {
|
||||
int32_t cur_timeout;
|
||||
|
||||
if (e->data.user && e->timeout != ~0) {
|
||||
cur_timeout = e->timeout - cur_time;
|
||||
if (cur_timeout <= 0) {
|
||||
e->data.user = false;
|
||||
e->data.dscp = e->data.file_dscp;
|
||||
} else if (!timeout || cur_timeout < timeout) {
|
||||
timeout = cur_timeout;
|
||||
next_timeout = e->timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (e->data.file || e->data.user)
|
||||
continue;
|
||||
|
||||
avl_delete(&map_data, &e->avl);
|
||||
fd = qosify_map_fds[e->data.id];
|
||||
bpf_map_delete_elem(fd, &e->data.addr);
|
||||
free(e);
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
uloop_timeout_set(&qosify_map_timer, timeout * 1000);
|
||||
}
|
||||
|
||||
void qosify_map_dump(struct blob_buf *b)
|
||||
{
|
||||
struct qosify_map_entry *e;
|
||||
uint32_t cur_time = qosify_gettime();
|
||||
int buf_len = INET6_ADDRSTRLEN + 1;
|
||||
char *buf;
|
||||
void *a;
|
||||
int af;
|
||||
|
||||
a = blobmsg_open_array(b, "entries");
|
||||
avl_for_each_element(&map_data, e, avl) {
|
||||
void *c;
|
||||
|
||||
if (!e->data.file && !e->data.user)
|
||||
continue;
|
||||
|
||||
c = blobmsg_open_table(b, NULL);
|
||||
if (e->data.user && e->timeout != ~0) {
|
||||
int32_t cur_timeout = e->timeout - cur_time;
|
||||
|
||||
if (cur_timeout < 0)
|
||||
cur_timeout = 0;
|
||||
|
||||
blobmsg_add_u32(b, "timeout", cur_timeout);
|
||||
}
|
||||
|
||||
blobmsg_add_u8(b, "file", e->data.file);
|
||||
blobmsg_add_u8(b, "user", e->data.user);
|
||||
|
||||
blobmsg_add_string(b, "type", qosify_map_info[e->data.id].type_name);
|
||||
|
||||
buf = blobmsg_alloc_string_buffer(b, "value", buf_len);
|
||||
switch (e->data.id) {
|
||||
case CL_MAP_TCP_PORTS:
|
||||
case CL_MAP_UDP_PORTS:
|
||||
snprintf(buf, buf_len, "%d", ntohs(e->data.addr.port));
|
||||
break;
|
||||
case CL_MAP_IPV4_ADDR:
|
||||
case CL_MAP_IPV6_ADDR:
|
||||
af = e->data.id == CL_MAP_IPV6_ADDR ? AF_INET6 : AF_INET;
|
||||
inet_ntop(af, &e->data.addr, buf, buf_len);
|
||||
break;
|
||||
default:
|
||||
*buf = 0;
|
||||
break;
|
||||
}
|
||||
blobmsg_add_string_buffer(b);
|
||||
blobmsg_close_table(b, c);
|
||||
}
|
||||
blobmsg_close_array(b, a);
|
||||
}
|
||||
|
||||
void qosify_map_update_config(void)
|
||||
{
|
||||
int fd = qosify_map_fds[CL_MAP_CONFIG];
|
||||
uint32_t key = 0;
|
||||
|
||||
bpf_map_update_elem(fd, &key, &config, BPF_ANY);
|
||||
}
|
||||
433
feeds/ucentral/qosify/src/qosify-bpf.c
Normal file
433
feeds/ucentral/qosify/src/qosify-bpf.c
Normal file
@@ -0,0 +1,433 @@
|
||||
#define KBUILD_MODNAME "foo"
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <uapi/linux/if_ether.h>
|
||||
#include <uapi/linux/if_packet.h>
|
||||
#include <uapi/linux/ip.h>
|
||||
#include <uapi/linux/ipv6.h>
|
||||
#include <uapi/linux/in.h>
|
||||
#include <uapi/linux/tcp.h>
|
||||
#include <uapi/linux/udp.h>
|
||||
#include <uapi/linux/filter.h>
|
||||
#include <uapi/linux/pkt_cls.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "qosify-bpf.h"
|
||||
|
||||
#define INET_ECN_MASK 3
|
||||
#define DSCP_FALLBACK_FLAG BIT(6)
|
||||
|
||||
#define FLOW_CHECK_INTERVAL ((u32)((1000000000ULL) >> 24))
|
||||
#define FLOW_TIMEOUT ((u32)((30ULL * 1000000000ULL) >> 24))
|
||||
#define FLOW_BULK_TIMEOUT 5
|
||||
|
||||
#define EWMA_SHIFT 12
|
||||
|
||||
const volatile static uint32_t module_flags = 0;
|
||||
|
||||
struct flow_bucket {
|
||||
__u32 last_update;
|
||||
__u32 pkt_len_avg;
|
||||
__u16 pkt_count;
|
||||
__u8 dscp;
|
||||
__u8 bulk_timeout;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(pinning, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, struct qosify_config);
|
||||
__uint(max_entries, 1);
|
||||
} config SEC(".maps");
|
||||
|
||||
typedef struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(pinning, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u8);
|
||||
__uint(max_entries, 1 << 16);
|
||||
} port_array_t;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__uint(pinning, 1);
|
||||
__type(key, __u32);
|
||||
__uint(value_size, sizeof(struct flow_bucket));
|
||||
__uint(max_entries, QOSIFY_FLOW_BUCKETS);
|
||||
} flow_map SEC(".maps");
|
||||
|
||||
port_array_t tcp_ports SEC(".maps");
|
||||
port_array_t udp_ports SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(pinning, 1);
|
||||
__uint(key_size, sizeof(struct in_addr));
|
||||
__type(value, __u8);
|
||||
__uint(max_entries, 100000);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
} ipv4_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(pinning, 1);
|
||||
__uint(key_size, sizeof(struct in6_addr));
|
||||
__type(value, __u8);
|
||||
__uint(max_entries, 100000);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
} ipv6_map SEC(".maps");
|
||||
|
||||
static struct qosify_config *get_config(void)
|
||||
{
|
||||
__u32 key = 0;
|
||||
|
||||
return bpf_map_lookup_elem(&config, &key);
|
||||
}
|
||||
|
||||
static __always_inline int proto_is_vlan(__u16 h_proto)
|
||||
{
|
||||
return !!(h_proto == bpf_htons(ETH_P_8021Q) ||
|
||||
h_proto == bpf_htons(ETH_P_8021AD));
|
||||
}
|
||||
|
||||
static __always_inline int proto_is_ip(__u16 h_proto)
|
||||
{
|
||||
return !!(h_proto == bpf_htons(ETH_P_IP) ||
|
||||
h_proto == bpf_htons(ETH_P_IPV6));
|
||||
}
|
||||
|
||||
static __always_inline void *skb_ptr(struct __sk_buff *skb, __u32 offset)
|
||||
{
|
||||
void *start = (void *)(unsigned long long)skb->data;
|
||||
|
||||
return start + offset;
|
||||
}
|
||||
|
||||
static __always_inline void *skb_end_ptr(struct __sk_buff *skb)
|
||||
{
|
||||
return (void *)(unsigned long long)skb->data_end;
|
||||
}
|
||||
|
||||
static __always_inline int skb_check(struct __sk_buff *skb, void *ptr)
|
||||
{
|
||||
if (ptr > skb_end_ptr(skb))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __always_inline __u32 cur_time(void)
|
||||
{
|
||||
__u32 val = bpf_ktime_get_ns() >> 24;
|
||||
|
||||
if (!val)
|
||||
val = 1;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static __always_inline __u32 ewma(__u32 *avg, __u32 val)
|
||||
{
|
||||
if (*avg)
|
||||
*avg = (*avg * 3) / 4 + (val << EWMA_SHIFT) / 4;
|
||||
else
|
||||
*avg = val << EWMA_SHIFT;
|
||||
|
||||
return *avg >> EWMA_SHIFT;
|
||||
}
|
||||
|
||||
static __always_inline void
|
||||
ipv4_change_dsfield(struct iphdr *iph, __u8 mask, __u8 value, bool force)
|
||||
{
|
||||
__u32 check = bpf_ntohs(iph->check);
|
||||
__u8 dsfield;
|
||||
|
||||
if ((iph->tos & mask) && !force)
|
||||
return;
|
||||
|
||||
dsfield = (iph->tos & mask) | value;
|
||||
if (iph->tos == dsfield)
|
||||
return;
|
||||
|
||||
check += iph->tos;
|
||||
if ((check + 1) >> 16)
|
||||
check = (check + 1) & 0xffff;
|
||||
check -= dsfield;
|
||||
check += check >> 16;
|
||||
iph->check = bpf_htons(check);
|
||||
iph->tos = dsfield;
|
||||
}
|
||||
|
||||
static __always_inline void
|
||||
ipv6_change_dsfield(struct ipv6hdr *ipv6h, __u8 mask, __u8 value, bool force)
|
||||
{
|
||||
__u16 *p = (__u16 *)ipv6h;
|
||||
__u16 val;
|
||||
|
||||
if (((*p >> 4) & mask) && !force)
|
||||
return;
|
||||
|
||||
val = (*p & bpf_htons((((__u16)mask << 4) | 0xf00f))) | bpf_htons((__u16)value << 4);
|
||||
if (val == *p)
|
||||
return;
|
||||
|
||||
*p = val;
|
||||
}
|
||||
|
||||
static __always_inline int
|
||||
parse_ethernet(struct __sk_buff *skb, __u32 *offset)
|
||||
{
|
||||
struct ethhdr *eth;
|
||||
__u16 h_proto;
|
||||
int i;
|
||||
|
||||
eth = skb_ptr(skb, *offset);
|
||||
if (skb_check(skb, eth + 1))
|
||||
return -1;
|
||||
|
||||
h_proto = eth->h_proto;
|
||||
*offset += sizeof(*eth);
|
||||
|
||||
#pragma unroll
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct vlan_hdr *vlh = skb_ptr(skb, *offset);
|
||||
|
||||
if (!proto_is_vlan(h_proto))
|
||||
break;
|
||||
|
||||
if (skb_check(skb, vlh + 1))
|
||||
return -1;
|
||||
|
||||
h_proto = vlh->h_vlan_encapsulated_proto;
|
||||
*offset += sizeof(*vlh);
|
||||
}
|
||||
|
||||
return h_proto;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_l4proto(struct qosify_config *config, struct __sk_buff *skb,
|
||||
__u32 offset, __u8 proto, __u8 *dscp_out)
|
||||
{
|
||||
struct udphdr *udp = skb_ptr(skb, offset);
|
||||
__u32 key;
|
||||
__u8 *value;
|
||||
|
||||
if (skb_check(skb, &udp->len))
|
||||
return;
|
||||
|
||||
if (module_flags & QOSIFY_INGRESS)
|
||||
key = udp->source;
|
||||
else
|
||||
key = udp->dest;
|
||||
|
||||
if (proto == IPPROTO_TCP)
|
||||
value = bpf_map_lookup_elem(&tcp_ports, &key);
|
||||
else if (proto == IPPROTO_UDP)
|
||||
value = bpf_map_lookup_elem(&udp_ports, &key);
|
||||
else {
|
||||
if ((proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) &&
|
||||
config && config->dscp_icmp != 0xff)
|
||||
*dscp_out = config->dscp_icmp;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
if ((*value & DSCP_FALLBACK_FLAG) && *dscp_out)
|
||||
*dscp_out = *value;
|
||||
}
|
||||
|
||||
static void
|
||||
check_flow(struct qosify_config *config, struct __sk_buff *skb,
|
||||
uint8_t *dscp)
|
||||
{
|
||||
struct flow_bucket flow_data;
|
||||
struct flow_bucket *flow;
|
||||
__s32 delta;
|
||||
__u32 hash;
|
||||
__u32 time;
|
||||
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
time = cur_time();
|
||||
hash = bpf_get_hash_recalc(skb);
|
||||
flow = bpf_map_lookup_elem(&flow_map, &hash);
|
||||
if (!flow) {
|
||||
memset(&flow_data, 0, sizeof(flow_data));
|
||||
bpf_map_update_elem(&flow_map, &hash, &flow_data, BPF_ANY);
|
||||
flow = bpf_map_lookup_elem(&flow_map, &hash);
|
||||
if (!flow)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!flow->last_update)
|
||||
goto reset;
|
||||
|
||||
delta = time - flow->last_update;
|
||||
if ((u32)delta > FLOW_TIMEOUT)
|
||||
goto reset;
|
||||
|
||||
if (delta >= FLOW_CHECK_INTERVAL) {
|
||||
if (flow->bulk_timeout) {
|
||||
flow->bulk_timeout--;
|
||||
if (!flow->bulk_timeout)
|
||||
flow->dscp = 0xff;
|
||||
}
|
||||
|
||||
goto clear;
|
||||
}
|
||||
|
||||
if (flow->pkt_count < 0xffff)
|
||||
flow->pkt_count++;
|
||||
|
||||
if (flow->pkt_count > config->bulk_trigger_pps) {
|
||||
flow->dscp = config->dscp_bulk;
|
||||
flow->bulk_timeout = config->bulk_trigger_timeout;
|
||||
}
|
||||
|
||||
out:
|
||||
if (config->prio_max_avg_pkt_len &&
|
||||
flow->dscp != config->dscp_bulk) {
|
||||
if (ewma(&flow->pkt_len_avg, skb->len) <
|
||||
config->prio_max_avg_pkt_len)
|
||||
flow->dscp = config->dscp_prio;
|
||||
else
|
||||
flow->dscp = 0xff;
|
||||
}
|
||||
|
||||
if (flow->dscp != 0xff &&
|
||||
!(*dscp && (flow->dscp & DSCP_FALLBACK_FLAG)))
|
||||
*dscp = flow->dscp;
|
||||
|
||||
return;
|
||||
|
||||
reset:
|
||||
flow->dscp = 0xff;
|
||||
flow->pkt_len_avg = 0;
|
||||
clear:
|
||||
flow->pkt_count = 1;
|
||||
flow->last_update = time;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
static __always_inline void
|
||||
parse_ipv4(struct __sk_buff *skb, __u32 *offset)
|
||||
{
|
||||
struct qosify_config *config;
|
||||
const __u32 zero_port = 0;
|
||||
struct iphdr *iph;
|
||||
__u8 dscp = 0;
|
||||
__u8 *value;
|
||||
int hdr_len;
|
||||
void *key;
|
||||
bool force;
|
||||
|
||||
config = get_config();
|
||||
|
||||
iph = skb_ptr(skb, *offset);
|
||||
if (skb_check(skb, iph + 1))
|
||||
return;
|
||||
|
||||
hdr_len = iph->ihl * 4;
|
||||
if (bpf_skb_pull_data(skb, *offset + hdr_len))
|
||||
return;
|
||||
|
||||
iph = skb_ptr(skb, *offset);
|
||||
*offset += hdr_len;
|
||||
|
||||
if (skb_check(skb, (void *)(iph + 1)))
|
||||
return;
|
||||
|
||||
parse_l4proto(config, skb, *offset, iph->protocol, &dscp);
|
||||
|
||||
if (module_flags & QOSIFY_INGRESS)
|
||||
key = &iph->saddr;
|
||||
else
|
||||
key = &iph->daddr;
|
||||
|
||||
value = bpf_map_lookup_elem(&ipv4_map, key);
|
||||
/* use udp port 0 entry as fallback for non-tcp/udp */
|
||||
if (!value)
|
||||
value = bpf_map_lookup_elem(&udp_ports, &zero_port);
|
||||
if (value)
|
||||
dscp = *value;
|
||||
|
||||
check_flow(config, skb, &dscp);
|
||||
|
||||
force = !(dscp & DSCP_FALLBACK_FLAG);
|
||||
dscp &= GENMASK(5, 0);
|
||||
|
||||
ipv4_change_dsfield(iph, INET_ECN_MASK, dscp << 2, force);
|
||||
}
|
||||
|
||||
static __always_inline void
|
||||
parse_ipv6(struct __sk_buff *skb, __u32 *offset)
|
||||
{
|
||||
struct qosify_config *config;
|
||||
const __u32 zero_port = 0;
|
||||
struct ipv6hdr *iph;
|
||||
__u8 dscp = 0;
|
||||
__u8 *value;
|
||||
void *key;
|
||||
bool force;
|
||||
|
||||
config = get_config();
|
||||
|
||||
if (bpf_skb_pull_data(skb, *offset + sizeof(*iph)))
|
||||
return;
|
||||
|
||||
iph = skb_ptr(skb, *offset);
|
||||
*offset += sizeof(*iph);
|
||||
|
||||
if (skb_check(skb, (void *)(iph + 1)))
|
||||
return;
|
||||
|
||||
if (module_flags & QOSIFY_INGRESS)
|
||||
key = &iph->saddr;
|
||||
else
|
||||
key = &iph->daddr;
|
||||
|
||||
parse_l4proto(config, skb, *offset, iph->nexthdr, &dscp);
|
||||
|
||||
value = bpf_map_lookup_elem(&ipv6_map, key);
|
||||
|
||||
/* use udp port 0 entry as fallback for non-tcp/udp */
|
||||
if (!value)
|
||||
value = bpf_map_lookup_elem(&udp_ports, &zero_port);
|
||||
if (value)
|
||||
dscp = *value;
|
||||
|
||||
check_flow(config, skb, &dscp);
|
||||
|
||||
force = !(dscp & DSCP_FALLBACK_FLAG);
|
||||
dscp &= GENMASK(5, 0);
|
||||
|
||||
ipv6_change_dsfield(iph, INET_ECN_MASK, dscp << 2, force);
|
||||
}
|
||||
|
||||
SEC("classifier")
|
||||
int classify(struct __sk_buff *skb)
|
||||
{
|
||||
__u32 offset = 0;
|
||||
int type;
|
||||
|
||||
if (module_flags & QOSIFY_IP_ONLY)
|
||||
type = skb->protocol;
|
||||
else
|
||||
type = parse_ethernet(skb, &offset);
|
||||
|
||||
if (type == bpf_htons(ETH_P_IP))
|
||||
parse_ipv4(skb, &offset);
|
||||
else if (type == bpf_htons(ETH_P_IPV6))
|
||||
parse_ipv6(skb, &offset);
|
||||
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
26
feeds/ucentral/qosify/src/qosify-bpf.h
Normal file
26
feeds/ucentral/qosify/src/qosify-bpf.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __BPF_QOSIFY_H
|
||||
#define __BPF_QOSIFY_H
|
||||
|
||||
#ifndef QOSIFY_FLOW_BUCKET_SHIFT
|
||||
#define QOSIFY_FLOW_BUCKET_SHIFT 13
|
||||
#endif
|
||||
|
||||
#define QOSIFY_FLOW_BUCKETS (1 << QOSIFY_FLOW_BUCKET_SHIFT)
|
||||
|
||||
/* rodata per-instance flags */
|
||||
#define QOSIFY_INGRESS (1 << 0)
|
||||
#define QOSIFY_IP_ONLY (1 << 1)
|
||||
|
||||
/* global config data */
|
||||
struct qosify_config {
|
||||
uint8_t dscp_prio;
|
||||
uint8_t dscp_bulk;
|
||||
uint8_t dscp_icmp;
|
||||
|
||||
uint8_t bulk_trigger_timeout;
|
||||
uint16_t bulk_trigger_pps;
|
||||
|
||||
uint16_t prio_max_avg_pkt_len;
|
||||
};
|
||||
|
||||
#endif
|
||||
82
feeds/ucentral/qosify/src/qosify.h
Normal file
82
feeds/ucentral/qosify/src/qosify.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef __QOS_CLASSIFY_H
|
||||
#define __QOS_CLASSIFY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "qosify-bpf.h"
|
||||
|
||||
#include <libubox/utils.h>
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define CLASSIFY_PROG_PATH "/lib/bpf/qosify-bpf.o"
|
||||
#define CLASSIFY_PIN_PATH "/sys/fs/bpf/qosify"
|
||||
#define CLASSIFY_DATA_PATH "/sys/fs/bpf/qosify_data"
|
||||
|
||||
enum qosify_map_id {
|
||||
CL_MAP_TCP_PORTS,
|
||||
CL_MAP_UDP_PORTS,
|
||||
CL_MAP_IPV4_ADDR,
|
||||
CL_MAP_IPV6_ADDR,
|
||||
CL_MAP_CONFIG,
|
||||
__CL_MAP_MAX,
|
||||
};
|
||||
|
||||
struct qosify_map_data {
|
||||
enum qosify_map_id id;
|
||||
|
||||
bool file : 1;
|
||||
bool user : 1;
|
||||
|
||||
uint8_t dscp;
|
||||
uint8_t file_dscp;
|
||||
|
||||
union {
|
||||
uint16_t port;
|
||||
struct in_addr ip;
|
||||
struct in6_addr ip6;
|
||||
} addr;
|
||||
};
|
||||
|
||||
struct qosify_map_entry {
|
||||
struct avl_node avl;
|
||||
|
||||
uint32_t timeout;
|
||||
|
||||
struct qosify_map_data data;
|
||||
};
|
||||
|
||||
|
||||
extern int qosify_map_timeout;
|
||||
extern struct qosify_config config;
|
||||
|
||||
int qosify_loader_init(bool force_init);
|
||||
|
||||
int qosify_map_init(void);
|
||||
int qosify_map_dscp_value(const char *val);
|
||||
int qosify_map_load_file(const char *file);
|
||||
int qosify_map_set_entry(enum qosify_map_id id, bool file, const char *str, uint8_t dscp);
|
||||
void qosify_map_reload(void);
|
||||
void qosify_map_clear_files(void);
|
||||
void qosify_map_gc(void);
|
||||
void qosify_map_dump(struct blob_buf *b);
|
||||
void qosify_map_set_dscp_default(enum qosify_map_id id, uint8_t val);
|
||||
void qosify_map_reset_config(void);
|
||||
void qosify_map_update_config(void);
|
||||
|
||||
int qosify_iface_init(void);
|
||||
void qosify_iface_config_update(struct blob_attr *ifaces, struct blob_attr *devs);
|
||||
void qosify_iface_check(void);
|
||||
void qosify_iface_status(struct blob_buf *b);
|
||||
void qosify_iface_stop(void);
|
||||
|
||||
int qosify_ubus_init(void);
|
||||
int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len);
|
||||
|
||||
#endif
|
||||
364
feeds/ucentral/qosify/src/ubus.c
Normal file
364
feeds/ucentral/qosify/src/ubus.c
Normal file
@@ -0,0 +1,364 @@
|
||||
#include <libubus.h>
|
||||
|
||||
#include "qosify.h"
|
||||
|
||||
static struct blob_buf b;
|
||||
|
||||
static int
|
||||
qosify_ubus_add_array(struct blob_attr *attr, uint8_t val, enum qosify_map_id id)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_for_each_attr(cur, attr, rem)
|
||||
qosify_map_set_entry(id, false, blobmsg_get_string(cur), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_ubus_set_files(struct blob_attr *attr)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
if (blobmsg_check_array(attr, BLOBMSG_TYPE_STRING) < 0)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
qosify_map_clear_files();
|
||||
|
||||
blobmsg_for_each_attr(cur, attr, rem)
|
||||
qosify_map_load_file(blobmsg_get_string(cur));
|
||||
|
||||
qosify_map_gc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
CL_ADD_DSCP,
|
||||
CL_ADD_TIMEOUT,
|
||||
CL_ADD_IPV4,
|
||||
CL_ADD_IPV6,
|
||||
CL_ADD_TCP_PORT,
|
||||
CL_ADD_UDP_PORT,
|
||||
__CL_ADD_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy qosify_add_policy[__CL_ADD_MAX] = {
|
||||
[CL_ADD_DSCP] = { "dscp", BLOBMSG_TYPE_STRING },
|
||||
[CL_ADD_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
|
||||
[CL_ADD_IPV4] = { "ipv4", BLOBMSG_TYPE_ARRAY },
|
||||
[CL_ADD_IPV6] = { "ipv6", BLOBMSG_TYPE_ARRAY },
|
||||
[CL_ADD_TCP_PORT] = { "tcp_port", BLOBMSG_TYPE_ARRAY },
|
||||
[CL_ADD_UDP_PORT] = { "udp_port", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
qosify_ubus_reload(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
qosify_map_reload();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qosify_ubus_add(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
int prev_timemout = qosify_map_timeout;
|
||||
struct blob_attr *tb[__CL_ADD_MAX];
|
||||
struct blob_attr *cur;
|
||||
int dscp = -1;
|
||||
int ret;
|
||||
|
||||
blobmsg_parse(qosify_add_policy, __CL_ADD_MAX, tb,
|
||||
blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (!strcmp(method, "add")) {
|
||||
if ((cur = tb[CL_ADD_DSCP]) != NULL)
|
||||
dscp = qosify_map_dscp_value(blobmsg_get_string(cur));
|
||||
else
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
if (dscp < 0)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if ((cur = tb[CL_ADD_TIMEOUT]) != NULL)
|
||||
qosify_map_timeout = blobmsg_get_u32(cur);
|
||||
} else {
|
||||
dscp = 0xff;
|
||||
}
|
||||
|
||||
if ((cur = tb[CL_ADD_IPV4]) != NULL &&
|
||||
(ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV4_ADDR) != 0))
|
||||
return ret;
|
||||
|
||||
if ((cur = tb[CL_ADD_IPV6]) != NULL &&
|
||||
(ret = qosify_ubus_add_array(cur, dscp, CL_MAP_IPV6_ADDR) != 0))
|
||||
return ret;
|
||||
|
||||
if ((cur = tb[CL_ADD_TCP_PORT]) != NULL &&
|
||||
(ret = qosify_ubus_add_array(cur, dscp, CL_MAP_TCP_PORTS) != 0))
|
||||
return ret;
|
||||
|
||||
if ((cur = tb[CL_ADD_UDP_PORT]) != NULL &&
|
||||
(ret = qosify_ubus_add_array(cur, dscp, CL_MAP_UDP_PORTS) != 0))
|
||||
return ret;
|
||||
|
||||
qosify_map_timeout = prev_timemout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
CL_CONFIG_RESET,
|
||||
CL_CONFIG_FILES,
|
||||
CL_CONFIG_TIMEOUT,
|
||||
CL_CONFIG_DSCP_UDP,
|
||||
CL_CONFIG_DSCP_TCP,
|
||||
CL_CONFIG_DSCP_PRIO,
|
||||
CL_CONFIG_DSCP_BULK,
|
||||
CL_CONFIG_DSCP_ICMP,
|
||||
CL_CONFIG_BULK_TIMEOUT,
|
||||
CL_CONFIG_BULK_PPS,
|
||||
CL_CONFIG_PRIO_PKT_LEN,
|
||||
CL_CONFIG_INTERFACES,
|
||||
CL_CONFIG_DEVICES,
|
||||
__CL_CONFIG_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy qosify_config_policy[__CL_CONFIG_MAX] = {
|
||||
[CL_CONFIG_RESET] = { "reset", BLOBMSG_TYPE_BOOL },
|
||||
[CL_CONFIG_FILES] = { "files", BLOBMSG_TYPE_ARRAY },
|
||||
[CL_CONFIG_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
|
||||
[CL_CONFIG_DSCP_UDP] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING },
|
||||
[CL_CONFIG_DSCP_TCP] = { "dscp_default_udp", BLOBMSG_TYPE_STRING },
|
||||
[CL_CONFIG_DSCP_PRIO] = { "dscp_prio", BLOBMSG_TYPE_STRING },
|
||||
[CL_CONFIG_DSCP_BULK] = { "dscp_bulk", BLOBMSG_TYPE_STRING },
|
||||
[CL_CONFIG_DSCP_ICMP] = { "dscp_icmp", BLOBMSG_TYPE_STRING },
|
||||
[CL_CONFIG_BULK_TIMEOUT] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32 },
|
||||
[CL_CONFIG_BULK_PPS] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32 },
|
||||
[CL_CONFIG_PRIO_PKT_LEN] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32 },
|
||||
[CL_CONFIG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_TABLE },
|
||||
[CL_CONFIG_DEVICES] = { "devices", BLOBMSG_TYPE_TABLE },
|
||||
};
|
||||
|
||||
static int __set_dscp(uint8_t *dest, struct blob_attr *attr, bool reset)
|
||||
{
|
||||
int dscp;
|
||||
|
||||
if (reset)
|
||||
*dest = 0xff;
|
||||
|
||||
if (!attr)
|
||||
return 0;
|
||||
|
||||
dscp = qosify_map_dscp_value(blobmsg_get_string(attr));
|
||||
if (dscp < 0)
|
||||
return -1;
|
||||
|
||||
*dest = dscp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__CL_CONFIG_MAX];
|
||||
struct blob_attr *cur;
|
||||
uint8_t dscp;
|
||||
bool reset = false;
|
||||
int ret;
|
||||
|
||||
blobmsg_parse(qosify_config_policy, __CL_CONFIG_MAX, tb,
|
||||
blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if ((cur = tb[CL_CONFIG_RESET]) != NULL)
|
||||
reset = blobmsg_get_bool(cur);
|
||||
|
||||
if (reset)
|
||||
qosify_map_reset_config();
|
||||
|
||||
if ((cur = tb[CL_CONFIG_TIMEOUT]) != NULL)
|
||||
qosify_map_timeout = blobmsg_get_u32(cur);
|
||||
|
||||
if ((cur = tb[CL_CONFIG_FILES]) != NULL &&
|
||||
(ret = qosify_ubus_set_files(cur) != 0))
|
||||
return ret;
|
||||
|
||||
__set_dscp(&dscp, tb[CL_CONFIG_DSCP_UDP], true);
|
||||
if (dscp != 0xff)
|
||||
qosify_map_set_dscp_default(CL_MAP_UDP_PORTS, dscp);
|
||||
|
||||
__set_dscp(&dscp, tb[CL_CONFIG_DSCP_TCP], true);
|
||||
if (dscp != 0xff)
|
||||
qosify_map_set_dscp_default(CL_MAP_TCP_PORTS, dscp);
|
||||
|
||||
__set_dscp(&config.dscp_prio, tb[CL_CONFIG_DSCP_PRIO], reset);
|
||||
__set_dscp(&config.dscp_bulk, tb[CL_CONFIG_DSCP_BULK], reset);
|
||||
__set_dscp(&config.dscp_icmp, tb[CL_CONFIG_DSCP_ICMP], reset);
|
||||
|
||||
if ((cur = tb[CL_CONFIG_BULK_TIMEOUT]) != NULL)
|
||||
config.bulk_trigger_timeout = blobmsg_get_u32(cur);
|
||||
|
||||
if ((cur = tb[CL_CONFIG_BULK_PPS]) != NULL)
|
||||
config.bulk_trigger_pps = blobmsg_get_u32(cur);
|
||||
|
||||
if ((cur = tb[CL_CONFIG_PRIO_PKT_LEN]) != NULL)
|
||||
config.prio_max_avg_pkt_len = blobmsg_get_u32(cur);
|
||||
|
||||
qosify_map_update_config();
|
||||
|
||||
qosify_iface_config_update(tb[CL_CONFIG_INTERFACES], tb[CL_CONFIG_DEVICES]);
|
||||
|
||||
qosify_iface_check();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qosify_ubus_dump(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
blob_buf_init(&b, 0);
|
||||
qosify_map_dump(&b);
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
blob_buf_free(&b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qosify_ubus_status(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
blob_buf_init(&b, 0);
|
||||
qosify_iface_status(&b);
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
blob_buf_free(&b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
CL_DEV_EVENT_NAME,
|
||||
CL_DEV_EVENT_ADD,
|
||||
__CL_DEV_EVENT_MAX,
|
||||
};
|
||||
|
||||
static int
|
||||
qosify_ubus_check_devices(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
qosify_iface_check();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct ubus_method qosify_methods[] = {
|
||||
UBUS_METHOD_NOARG("reload", qosify_ubus_reload),
|
||||
UBUS_METHOD("add", qosify_ubus_add, qosify_add_policy),
|
||||
UBUS_METHOD_MASK("remove", qosify_ubus_add, qosify_add_policy,
|
||||
((1 << __CL_ADD_MAX) - 1) & ~(1 << CL_ADD_DSCP)),
|
||||
UBUS_METHOD("config", qosify_ubus_config, qosify_config_policy),
|
||||
UBUS_METHOD_NOARG("dump", qosify_ubus_dump),
|
||||
UBUS_METHOD_NOARG("status", qosify_ubus_status),
|
||||
UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices),
|
||||
};
|
||||
|
||||
static struct ubus_object_type qosify_object_type =
|
||||
UBUS_OBJECT_TYPE("qosify", qosify_methods);
|
||||
|
||||
static struct ubus_object qosify_object = {
|
||||
.name = "qosify",
|
||||
.type = &qosify_object_type,
|
||||
.methods = qosify_methods,
|
||||
.n_methods = ARRAY_SIZE(qosify_methods),
|
||||
};
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
ubus_add_object(ctx, &qosify_object);
|
||||
}
|
||||
|
||||
static struct ubus_auto_conn conn;
|
||||
|
||||
int qosify_ubus_init(void)
|
||||
{
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iface_req {
|
||||
char *name;
|
||||
int len;
|
||||
};
|
||||
|
||||
static void
|
||||
netifd_if_cb(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
struct iface_req *ifr = req->priv;
|
||||
enum {
|
||||
IFS_ATTR_UP,
|
||||
IFS_ATTR_DEV,
|
||||
__IFS_ATTR_MAX
|
||||
};
|
||||
static const struct blobmsg_policy policy[__IFS_ATTR_MAX] = {
|
||||
[IFS_ATTR_UP] = { "up", BLOBMSG_TYPE_BOOL },
|
||||
[IFS_ATTR_DEV] = { "l3_device", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
struct blob_attr *tb[__IFS_ATTR_MAX];
|
||||
|
||||
blobmsg_parse(policy, __IFS_ATTR_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (!tb[IFS_ATTR_UP] || !tb[IFS_ATTR_DEV])
|
||||
return;
|
||||
|
||||
if (!blobmsg_get_bool(tb[IFS_ATTR_UP]))
|
||||
return;
|
||||
|
||||
snprintf(ifr->name, ifr->len, "%s", blobmsg_get_string(tb[IFS_ATTR_DEV]));
|
||||
}
|
||||
|
||||
int qosify_ubus_check_interface(const char *name, char *ifname, int ifname_len)
|
||||
{
|
||||
struct iface_req req = { ifname, ifname_len };
|
||||
char *obj_name = "network.interface.";
|
||||
uint32_t id;
|
||||
|
||||
#define PREFIX "network.interface."
|
||||
obj_name = alloca(sizeof(PREFIX) + strlen(name) + 1);
|
||||
sprintf(obj_name, PREFIX "%s", name);
|
||||
#undef PREFIX
|
||||
|
||||
ifname[0] = 0;
|
||||
|
||||
if (ubus_lookup_id(&conn.ctx, obj_name, &id))
|
||||
return -1;
|
||||
|
||||
ubus_invoke(&conn.ctx, id, "status", b.head, netifd_if_cb, &req, 1000);
|
||||
|
||||
if (!ifname[0])
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"uuid": 2,
|
||||
"globals": {
|
||||
"wireless-multimedia": {
|
||||
"UP0": [ "DF"],
|
||||
"UP1": [ "CS1" ],
|
||||
"UP2": [ "AF11", "AF12", "AF13" ],
|
||||
"UP3": [ "CS2", "AF21", "AF22", "AF23" ],
|
||||
"UP4": [ "CS3", "AF31", "AF32", "AF33" ],
|
||||
"UP5": [ "CS5", "AF41", "AF42", "AF43" ],
|
||||
"UP6": [ "CS4", "EF" ],
|
||||
"UP7": [ "CS6" ]
|
||||
}
|
||||
},
|
||||
"radios": [
|
||||
{
|
||||
"band": "2G",
|
||||
"country": "CA",
|
||||
"channel-mode": "HE",
|
||||
"channel-width": 80,
|
||||
"channel": 32
|
||||
}
|
||||
],
|
||||
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "WAN",
|
||||
"role": "upstream",
|
||||
"services": [ "lldp" ],
|
||||
"ethernet": [
|
||||
{
|
||||
"select-ports": [
|
||||
"WAN*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"ipv4": {
|
||||
"addressing": "dynamic"
|
||||
},
|
||||
"ssids": [
|
||||
{
|
||||
"name": "OpenWifi",
|
||||
"wifi-bands": [
|
||||
"2G"
|
||||
],
|
||||
"bss-mode": "ap",
|
||||
"encryption": {
|
||||
"proto": "psk2",
|
||||
"key": "OpenWifi",
|
||||
"ieee80211w": "optional"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "LAN",
|
||||
"role": "downstream",
|
||||
"services": [ "ssh", "lldp" ],
|
||||
"ethernet": [
|
||||
{
|
||||
"select-ports": [
|
||||
"LAN*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"ipv4": {
|
||||
"addressing": "static",
|
||||
"subnet": "192.168.1.1/24",
|
||||
"dhcp": {
|
||||
"lease-first": 10,
|
||||
"lease-count": 100,
|
||||
"lease-time": "6h"
|
||||
}
|
||||
},
|
||||
"ssids": [
|
||||
{
|
||||
"name": "OpenWifi",
|
||||
"wifi-bands": [
|
||||
"2G"
|
||||
],
|
||||
"bss-mode": "ap",
|
||||
"encryption": {
|
||||
"proto": "psk2",
|
||||
"key": "OpenWifi",
|
||||
"ieee80211w": "optional"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"statistics": {
|
||||
"interval": 120,
|
||||
"types": [ "ssids", "lldp", "clients" ]
|
||||
},
|
||||
"health": {
|
||||
"interval": 120
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"lldp": {
|
||||
"describe": "uCentral",
|
||||
"location": "universe"
|
||||
},
|
||||
"ssh": {
|
||||
"port": 22
|
||||
},
|
||||
"quality-of-service": {
|
||||
"select-ports": [ "WAN" ],
|
||||
"bandwidth_up": 1000,
|
||||
"bandwidth_down": 1000,
|
||||
"classifier": [
|
||||
{
|
||||
"dscp": "CS0",
|
||||
"ports": [
|
||||
{ "protocol": "any", "port": 53 },
|
||||
{ "protocol": "tcp", "port": 80 }
|
||||
],
|
||||
"dns": [
|
||||
"telecominfraproject.com"
|
||||
]
|
||||
}, {
|
||||
"dscp": "CS1",
|
||||
"ports": [
|
||||
{ "protocol": "any", "port": 53, "range-end": 80 },
|
||||
{ "protocol": "udp", "port": 80, "reclassify": true }
|
||||
],
|
||||
"dns": [
|
||||
"telecominfraproject.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci delete qosify.wan
|
||||
uci delete qosify.wandev
|
||||
uci set qosify.@defaults[-1].defaults=/tmp/qosify.conf
|
||||
Reference in New Issue
Block a user