nft-qos: add new package
authorRosy Song <rosysong@rosinson.com>
Fri, 3 Aug 2018 04:17:59 +0000 (12:17 +0800)
committerRosy Song <rosysong@rosinson.com>
Tue, 6 Nov 2018 08:13:28 +0000 (16:13 +0800)
This is the nftables implementation for qos on OpenWrt,
Currently, it has below features:

* Static QoS : setting limit rate for devices or global network.

* Dynamic/Auto QoS : setting limit rate according to the network
  bandwidth and adjust itself automatically (hotplug event).

* Traffic Priority : this feature is like traffic shaping under tc,
  it uses ingress hook to handle to packets here.

Signed-off-by: Rosy Song <rosysong@rosinson.com>
net/nft-qos/Makefile [new file with mode: 0644]
net/nft-qos/files/lib/core.sh [new file with mode: 0644]
net/nft-qos/files/lib/dynamic.sh [new file with mode: 0644]
net/nft-qos/files/lib/monitor.sh [new file with mode: 0644]
net/nft-qos/files/lib/priority.sh [new file with mode: 0644]
net/nft-qos/files/lib/static.sh [new file with mode: 0644]
net/nft-qos/files/nft-qos-dynamic.hotplug [new file with mode: 0644]
net/nft-qos/files/nft-qos-monitor.hotplug [new file with mode: 0644]
net/nft-qos/files/nft-qos.config [new file with mode: 0644]
net/nft-qos/files/nft-qos.init [new file with mode: 0755]

diff --git a/net/nft-qos/Makefile b/net/nft-qos/Makefile
new file mode 100644 (file)
index 0000000..206745b
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nft-qos
+PKG_VERSION:=1.0.0
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-2.0
+
+PKG_MAINTAINER:=Rosy Song <rosysong@rosinson.com>
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/nft-qos
+  SECTION:=utils
+  CATEGORY:=Base system
+  DEPENDS:=+nftables +kmod-nft-netdev +kmod-nft-bridge
+  TITLE:=QoS scripts over nftables
+endef
+
+define Package/nft-qos/description
+ This package provides implementation for qos over nftables.
+ Currently, static/dynamic qos and traffic shaping are supported.
+endef
+
+define Package/nft-qos/conffiles
+/etc/config/nft-qos
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/nft-qos/install
+       $(INSTALL_DIR) $(1)/lib/nft-qos
+       $(INSTALL_DATA) ./files/lib/* $(1)/lib/nft-qos/
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/nft-qos.config $(1)/etc/config/nft-qos
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/nft-qos.init $(1)/etc/init.d/nft-qos
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/dhcp
+       $(INSTALL_BIN) ./files/nft-qos-monitor.hotplug $(1)/etc/hotplug.d/dhcp/00-nft-qos-monitor
+       $(INSTALL_BIN) ./files/nft-qos-dynamic.hotplug $(1)/etc/hotplug.d/dhcp/01-nft-qos-dynamic
+endef
+
+$(eval $(call BuildPackage,nft-qos))
diff --git a/net/nft-qos/files/lib/core.sh b/net/nft-qos/files/lib/core.sh
new file mode 100644 (file)
index 0000000..d3c9d64
--- /dev/null
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+# for uci_validate_section()
+. /lib/functions/procd.sh
+
+NFT_QOS_HAS_BRIDGE=
+NFT_QOS_INET_FAMILY=ip
+NFT_QOS_SCRIPT_TEXT=
+NFT_QOS_SCRIPT_FILE=/tmp/qos.nft
+
+qosdef_appendx() { # <string to be appended>
+       NFT_QOS_SCRIPT_TEXT="$NFT_QOS_SCRIPT_TEXT""$1"
+}
+
+qosdef_append_chain_def() { # <type> <hook> <priority> <policy>
+       qosdef_appendx "\t\ttype $1 hook $2 priority $3; policy $4;\n"
+}
+
+qosdef_append_chain_ingress() { # <type> <device> <priority> <policy>
+       qosdef_appendx "\t\ttype $1 hook ingress device $2 priority $3; policy $4;\n"
+}
+
+# qosdef_append_rule_{MATCH}_{STATEMENT}
+qosdef_append_rule_ip_limit() { # <ipaddr> <operator> <unit> <rate>
+       local ipaddr=$1
+       local operator=$2
+       local unit=$3
+       local rate=$4
+
+       qosdef_appendx \
+           "\t\tip $operator $ipaddr limit rate over $rate $unit/second drop\n"
+}
+
+# qosdef_append_rule_{MATCH}_{POLICY}
+qosdef_append_rule_ip_policy() { # <operator> <ipaddr> <policy>
+       qosdef_appendx "\t\tip $1 $2 $3\n"
+}
+
+_handle_limit_whitelist() { # <value> <chain>
+       local ipaddr=$1
+       local operator
+
+       [ -z "$ipaddr" ] && return
+
+       case "$2" in
+               download) operator="daddr";;
+               upload) operator="saddr";;
+       esac
+
+       qosdef_append_rule_ip_policy $operator $ipaddr accept
+}
+
+qosdef_append_rule_limit_whitelist() { # <chain>
+       config_list_foreach default limit_whitelist _handle_limit_whitelist $1
+}
+
+qosdef_flush_table() { # <family> <table>
+       nft flush table $1 $2 2>/dev/null
+}
+
+qosdef_remove_table() { # <family> <table>
+       nft delete table $1 $2 2>/dev/null
+}
+
+qosdef_init_header() { # add header for nft script
+       qosdef_appendx "#!/usr/sbin/nft -f\n"
+       qosdef_appendx "# Copyright (C) 2018 rosysong@rosinson.com\n"
+       qosdef_appendx "#\n\n"
+}
+
+qosdef_init_env() {
+       # check interface type of lan
+       local lt="$(uci_get "network.lan.type")"
+       [ "$lt" = "bridge" ] && export NFT_QOS_HAS_BRIDGE="y"
+
+       # check if ipv6 support
+       [ -e /proc/sys/net/ipv6 ] && export NFT_QOS_INET_FAMILY="inet"
+}
+
+qosdef_clean_cache() {
+       rm -f $NFT_QOS_SCRIPT_FILE
+}
+
+qosdef_init_done() {
+       echo -e $NFT_QOS_SCRIPT_TEXT > $NFT_QOS_SCRIPT_FILE 2>/dev/null
+}
+
+qosdef_start() {
+       nft -f $NFT_QOS_SCRIPT_FILE 2>/dev/null
+}
diff --git a/net/nft-qos/files/lib/dynamic.sh b/net/nft-qos/files/lib/dynamic.sh
new file mode 100644 (file)
index 0000000..960ca52
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+. /lib/nft-qos/core.sh
+
+# return average rate for dhcp leases
+qosdef_dynamic_rate() { # <bandwidth>
+       local c=0 c6=0
+
+       [ ! -e /tmp/dhcp.leases -a \
+         ! -e /var/dhcp6.leases ] && return
+
+       [ -e /tmp/dhcp.leases ] && \
+         c=$(wc -l < /tmp/dhcp.leases 2>/dev/null)
+       [ -e /var/dhcp6.leases ] && \
+         c6=$(wc -l < /var/dhcp6.leases 2>/dev/null)
+       [ $c -eq 0 -a $c6 -eq 0 ] && \
+         { echo 12500; return; }
+
+       echo $(($1 / ($c + $c6)))
+}
+
+qosdef_append_chain_dym() { # <hook> <name> <bandwidth>
+       local cidr cidr6
+       local operator rate
+       local hook=$1 name=$2 bandwidth=$3
+
+       config_get cidr default 'dynamic_cidr'
+       config_get cidr6 default 'dynamic_cidr6'
+
+       [ -z "$cidr" -a -z "$cidr6" ] && return
+
+       case "$2" in
+               download) operator=daddr;;
+               upload) operator=saddr;;
+       esac
+
+       rate=$(qosdef_dynamic_rate $bandwidth)
+
+       qosdef_appendx "\tchain $name {\n"
+       qosdef_append_chain_def filter $hook 0 accept
+       qosdef_append_rule_limit_whitelist $name
+       [ -n "$cidr" ] && \
+               qosdef_append_rule_ip_limit $cidr $operator kbytes $rate
+       [ -n "$cidr6" ] && \
+               qosdef_append_rule_ip_limit $cidr6 $operator kbytes $rate
+       qosdef_appendx "\t}\n"
+}
+
+qosdef_flush_dynamic() {
+       qosdef_flush_table "$NFT_QOS_INET_FAMILY" nft-qos-dynamic
+}
+
+# init dynamic qos
+qosdef_init_dynamic() {
+       local dynamic_bw_up dynamic_bw_down limit_enable limit_type
+       local hook_ul="input" hook_dl="postrouting"
+
+       uci_validate_section nft-qos default default \
+               'limit_enable:bool:0' \
+               'limit_type:maxlength(8)' \
+               'dynamic_bw_up:uinteger:100' \
+               'dynamic_bw_down:uinteger:100'
+
+       [ $? -ne 0 ] && {
+               logger -t nft-qos-dynamic "validation failed"
+               return 1
+       }
+
+       [ $limit_enable -eq 0 -o \
+               "$limit_type" = "static" ] && return 1
+
+       # Transfer mbits/s to mbytes/s
+       # e.g. 100,000 kbits == 12,500 kbytes
+       dynamic_bw_up=$(($dynamic_bw_up * 1000 / 8))
+       dynamic_bw_down=$(($dynamic_bw_down * 1000 / 8))
+
+       [ -z "$NFT_QOS_HAS_BRIDGE" ] && {
+               hook_ul="postrouting"
+               hook_dl="input"
+       }
+
+       qosdef_appendx "table $NFT_QOS_INET_FAMILY nft-qos-dynamic {\n"
+       qosdef_append_chain_dym $hook_ul upload $dynamic_bw_up
+       qosdef_append_chain_dym $hook_dl download $dynamic_bw_down
+       qosdef_appendx "}\n"
+}
diff --git a/net/nft-qos/files/lib/monitor.sh b/net/nft-qos/files/lib/monitor.sh
new file mode 100644 (file)
index 0000000..d05943a
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+. /lib/nft-qos/core.sh
+
+qosdef_monitor_get_ip_handle() { # <family> <chain> <ip>
+       echo $(nft list chain $1 nft-qos-monitor $2 -a 2>/dev/null | grep $3 | awk '{print $11}')
+}
+
+qosdef_monitor_add() { # <mac> <ip> <hostname>
+       handle_dl=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY download $2)
+       [ -z "$handle_dl" ] && nft add rule $NFT_QOS_INET_FAMILY nft-qos-monitor download ip daddr $2 counter
+       handle_ul=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY upload $2)
+       [ -z "$handle_ul" ] && nft add rule $NFT_QOS_INET_FAMILY nft-qos-monitor upload ip saddr $2 counter
+}
+
+qosdef_monitor_del() { # <mac> <ip> <hostname>
+       local handle_dl handle_ul
+       handle_dl=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY download $2)
+       handle_ul=$(qosdef_monitor_get_ip_handle $NFT_QOS_INET_FAMILY upload $2)
+       [ -n "$handle_dl" ] && nft delete handle $handle_dl
+       [ -n "$handle_ul" ] && nft delete handle $handle_ul
+}
+
+# init qos monitor
+qosdef_init_monitor() {
+       local hook_ul="input" hook_dl="postrouting"
+
+       [ -z "$NFT_QOS_HAS_BRIDGE" ] && {
+               hook_ul="postrouting"
+               hook_dl="input"
+       }
+
+       nft add table $NFT_QOS_INET_FAMILY nft-qos-monitor
+       nft add chain $NFT_QOS_INET_FAMILY nft-qos-monitor upload { type filter hook $hook_ul priority 0\; }
+       nft add chain $NFT_QOS_INET_FAMILY nft-qos-monitor download { type filter hook $hook_dl priority 0\; }
+}
diff --git a/net/nft-qos/files/lib/priority.sh b/net/nft-qos/files/lib/priority.sh
new file mode 100644 (file)
index 0000000..59288b8
--- /dev/null
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+. /lib/functions/network.sh
+. /lib/nft-qos/core.sh
+
+P1=""; P2=""; P3=""; P4=""; P5=""; P6="";
+P7=""; P8=""; P9=""; P10=""; P11="";
+
+_qosdef_handle_protox() { # <priority> <rule>
+       case "$1" in
+               -400) P1="$P1""$2";;
+               -300) P2="$P2""$2";;
+               -225) P3="$P3""$2";;
+               -200) P4="$P4""$2";;
+               -150) P5="$P5""$2";;
+               -100) P6="$P6""$2";;
+               0) P7="$P7""$2";;
+               50) P8="$P8""$2";;
+               100) P9="$P9""$2";;
+               225) P10="$P10""$2";;
+               300) P11="$P11""$2";;
+       esac
+}
+
+qosdef_handle_protox() { # <section>
+       local proto prio srv
+
+       config_get proto $1 'protocol'
+       config_get prio $1 'priority'
+       config_get srv $1 'service'
+
+       [ -z "$proto" -o \
+               -z "$prio" -o \
+               -z "$srv" ] && return
+
+       _qosdef_handle_protox $prio \
+           "\t\t$proto dport { $srv } accept\n"
+}
+
+qosdef_append_rule_protox() { # <section>
+       config_foreach qosdef_handle_protox $1
+       qosdef_appendx \
+               "${P1}${P2}${P3}${P4}${P5}${P6}${P7}${P8}${P9}${P10}${P11}"
+}
+
+qosdef_append_chain_priority() { # <name> <section> <device>
+       local name=$1 device=$3
+
+       qosdef_appendx "\tchain $name {\n"
+       qosdef_append_chain_ingress filter $device 0 accept
+       qosdef_append_rule_protox $2
+       qosdef_appendx "\t}\n"
+}
+
+qosdef_remove_priority() {
+       qosdef_remove_table netdev nft-qos-priority
+}
+
+# init traffic priority
+qosdef_init_priority() {
+       local priority_enable priority_netdev ifname="br-lan"
+
+       uci_validate_section nft-qos default default \
+               'priority_enable:bool:0' \
+               'priority_netdev:maxlength(8)'
+
+       [ $? -ne 0 ] && {
+               logger -t nft-qos-priority "validation failed"
+               return 1
+       }
+
+       [ $priority_enable -eq 0 ] && return 1
+
+       case "$priority_netdev" in
+               lan) [ "$(uci_get network.lan.type)" != "bridge" ] && {
+                               network_get_device ifname "$priority_netdev" || \
+                               ifname="$(uci_get network.lan.ifname)"
+                       }
+               ;;
+               wan*) network_get_device ifname "$priority_netdev" || \
+                       ifname="$(uci_get network.$priority_netdev.ifname)"
+       esac
+
+       qosdef_appendx "table netdev nft-qos-priority {\n"
+       qosdef_append_chain_priority filter priority $ifname
+       qosdef_appendx "}\n"
+}
diff --git a/net/nft-qos/files/lib/static.sh b/net/nft-qos/files/lib/static.sh
new file mode 100644 (file)
index 0000000..cb56b49
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+. /lib/nft-qos/core.sh
+
+# append rule for static qos
+qosdef_append_rule_sta() { # <section> <operator> <default-unit> <default-rate>
+       local ipaddr unit rate
+       local operator=$2
+
+       config_get ipaddr $1 ipaddr
+       config_get unit $1 unit $3
+       config_get rate $1 rate $4
+
+       [ -z "$ipaddr" ] && return
+
+       qosdef_append_rule_ip_limit $ipaddr $operator $unit $rate
+}
+
+# append chain for static qos
+qosdef_append_chain_sta() { # <hook> <name> <section> <unit> <rate>
+       local hook=$1 name=$2
+       local config=$3 operator
+
+       case "$name" in
+               download) operator="daddr";;
+               upload) operator="saddr";;
+       esac
+
+       qosdef_appendx "\tchain $name {\n"
+       qosdef_append_chain_def filter $hook 0 accept
+       qosdef_append_rule_limit_whitelist $name
+       config_foreach qosdef_append_rule_sta $config $operator $4 $5
+       qosdef_appendx "\t}\n"
+}
+
+qosdef_flush_static() {
+       qosdef_flush_table "$NFT_QOS_INET_FAMILY" nft-qos-static
+}
+
+# static limit rate init
+qosdef_init_static() {
+       local unit_dl unit_ul rate_dl rate_ul
+       local limit_enable limit_type hook_ul="input" hook_dl="postrouting"
+
+       uci_validate_section nft-qos default default \
+               'limit_enable:bool:0' \
+               'limit_type:maxlength(8)' \
+               'static_unit_dl:string:kbytes' \
+               'static_unit_ul:string:kbytes' \
+               'static_rate_dl:uinteger:50' \
+               'static_rate_ul:uinteger:50'
+
+       [ $? -ne 0 ] && {
+               logger -t nft-qos-static "validation failed"
+               return 1
+       }
+
+       [ $limit_enable -eq 0 -o \
+               $limit_type = "dynamic" ] && return 1
+
+       [ -z "$NFT_QOS_HAS_BRIDGE" ] && {
+               hook_ul="postrouting"
+               hook_dl="input"
+       }
+
+       qosdef_appendx "table $NFT_QOS_INET_FAMILY nft-qos-static {\n"
+       qosdef_append_chain_sta $hook_ul upload upload $unit_ul $rate_ul
+       qosdef_append_chain_sta $hook_dl download download $unit_dl $rate_dl
+       qosdef_appendx "}\n"
+}
diff --git a/net/nft-qos/files/nft-qos-dynamic.hotplug b/net/nft-qos/files/nft-qos-dynamic.hotplug
new file mode 100644 (file)
index 0000000..fb38b7e
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright 2018 rosysong@rosinson.com
+#
+
+. /lib/functions.sh
+. /lib/nft-qos/core.sh
+. /lib/nft-qos/dynamic.sh
+
+NFT_QOS_DYNAMIC_ON=
+
+qosdef_validate_section_dynamic() {
+       local limit_enable limit_type
+
+       uci_validate_section nft-qos default default \
+               'limit_enable:bool:0' \
+               'limit_type:maxlength(8)'
+
+       [ $limit_enable -eq 1 -a \
+         "$limit_type" = "dynamic" ] && \
+           NFT_QOS_DYNAMIC_ON="y"
+}
+
+
+logger -t nft-qos-dynamic "ACTION=$ACTION, MACADDR=$MACADDR, IPADDR=$IPADDR, HOSTNAME=$HOSTNAME"
+
+case "$ACTION" in
+       add | update | remove)
+               qosdef_validate_section_dynamic
+               [ -z "$NFT_QOS_DYNAMIC_ON" ] && return
+
+               qosdef_init_env
+               qosdef_flush_dynamic
+
+               qosdef_init_header
+               qosdef_init_dynamic
+               qosdef_init_done
+               qosdef_start
+               ;;
+esac
diff --git a/net/nft-qos/files/nft-qos-monitor.hotplug b/net/nft-qos/files/nft-qos-monitor.hotplug
new file mode 100644 (file)
index 0000000..df04fa6
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Copyright 2018 rosysong@rosinson.com
+#
+
+. /lib/nft-qos/monitor.sh
+
+logger -t nft-qos-monitor "ACTION=$ACTION, MACADDR=$MACADDR, IPADDR=$IPADDR, HOSTNAME=$HOSTNAME"
+
+case "$ACTION" in
+       add | update) qosdef_init_env && qosdef_monitor_add $MACADDR $IPADDR $HOSTNAME;;
+       remove) qosdef_init_env && qosdef_monitor_del $MACADDR $IPADDR $HOSTNAME;;
+esac
diff --git a/net/nft-qos/files/nft-qos.config b/net/nft-qos/files/nft-qos.config
new file mode 100644 (file)
index 0000000..d189490
--- /dev/null
@@ -0,0 +1,106 @@
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+# This is the sample for nft-qos configuration file,
+# which will generate a nftables script in /tmp/qos.nft
+#
+
+# Getting Started
+# Official site      :
+#  https://netfilter.org/projects/nftables/index.html
+# What is nftables   :
+#  https://wiki.nftables.org/wiki-nftables/index.php/Main_Page
+#
+
+# Basic Operations
+# Configuring Tables :
+#  https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables
+# Configuring Chains :
+#  https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains
+# Configuring Rules  :
+#  https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management
+# Quick Reference (recommended)   :
+#  https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes
+#  https://netfilter.org/projects/nftables/manpage.html
+#
+
+config default default
+       # Enable Flag for limit rate
+       option limit_enable '1'
+
+       # Options for enable Static QoS (rate limit)
+       option limit_type 'static'
+       # Options for Static QoS (rate limit)
+       option static_unit_dl 'kbytes'
+       option static_unit_ul 'kbytes'
+       option static_rate_dl '50'
+       option static_rate_ul '50'
+
+       # Options for enable Dynamic QoS
+       # This option can not compatible with Static QoS
+       # option limit_type 'dynamic'
+
+       # For Dynamic QoS Samples (unit of bandwidth is Mbps):
+       option dynamic_cidr '192.168.1.0/24'
+       option dynamic_cidr6 'AAAA:BBBB::1/64'
+       option dynamic_bw_up '100'
+       option dynamic_bw_down '100'
+
+       # White list for static/dynamic limit
+       # list limit_whitelist '192.168.1.225'
+       # list limit_whitelist '192.168.1.0/24'
+       # list limit_whitelist 'ABCD:CDEF::1/64'
+
+       # Options for Traffic Priority
+       option priority_enable '0'
+       option priority_netdev 'lan'
+
+
+#
+# For Static QoS Rate Limit Samples :
+#
+# For Download :
+#config download
+#      option hostname 'My PC'
+#      option unit 'kbytes'
+#      option ipaddr '192.168.1.224'
+#      option rate '128'
+#
+# For Upload :
+#config upload
+#      option hostname 'office-pc'
+#      option unit 'mbytes'
+#      option ipaddr 'ABCD:FFED::1/64'
+#      option rate '1024'
+#
+#
+# Traffic Priority Samples :
+#
+# protocol : tcp, udp, udplite, sctp, dccp, tcp is default
+# priority : integer between 1-11, 1 is default
+# service : you can input a integer or service name, e.g. '22', '11-22', 'telnet', 'ssh, http, ftp', etc
+#
+#config priority
+#      option protocol 'tcp'
+#      option priority '-400'
+#      option service '23'
+#      option comment '?'
+#
+#config priority
+#      option protocol 'udp'
+#      option priority '-400'
+#      option service 'https'
+#      option comment '?'
+#
+#config priority
+#      option protocol 'dccp'
+#      option priority '0'
+#      option service '22-35'
+#      option comment '?'
+#
+#config priority
+#      option protocol 'dccp'
+#      option priority '300'
+#      option service 'ftp,ssh,http'
+#      option comment '?'
+#
diff --git a/net/nft-qos/files/nft-qos.init b/net/nft-qos/files/nft-qos.init
new file mode 100755 (executable)
index 0000000..e48418c
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh /etc/rc.common
+#
+# Copyright (C) 2018 rosysong@rosinson.com
+#
+
+. /lib/nft-qos/core.sh
+. /lib/nft-qos/monitor.sh
+. /lib/nft-qos/dynamic.sh
+. /lib/nft-qos/static.sh
+. /lib/nft-qos/priority.sh
+
+START=99
+USE_PROCD=1
+
+service_triggers() {
+       procd_add_reload_trigger nft-qos
+}
+
+start_service() {
+       config_load nft-qos
+
+       qosdef_init_env
+       qosdef_flush_static
+       qosdef_flush_dynamic
+       qosdef_remove_priority
+
+       qosdef_init_header
+       qosdef_init_monitor
+       qosdef_init_dynamic
+       qosdef_init_static
+       qosdef_init_priority
+       qosdef_init_done
+       qosdef_start
+}
+
+stop_service() {
+       qosdef_flush_dynamic
+       qosdef_flush_static
+       qosdef_remove_priority
+       qosdef_clean_cache
+}