diff --git a/README.md b/README.md
index 07b5a19..450713b 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,44 @@
-# What is DSCP Classify?
-DSCP Classify is an nftables based service for applying DSCP class to connections (this only works in OpenWrt 22.03 and above).
+# What is DSCP Classify? ⭐
+DSCP Classify is a service for applying DSCP class to connection packets (supporting **OpenWrt 22.03 and above**).\
+It can be used with SQM layer cake QoS to manage priority of client connections (VoIP/gaming/downloads/P2P etc) and reduce [Bufferbloat](https://en.wikipedia.org/wiki/Bufferbloat).
-This should be used in conjunction with layer-cake SQM queue with ctinfo configured to restore DSCP on the device ingress.
-The dscpclassify service uses the last 8 bits of the conntrack mark (0x000000ff).
+The service supports both **automatic** and **user rule** classification of connections.
-# Classification modes
-The service uses three methods for classifying and DSCP marking connections outlined below.
+DSCP Classify can mark LAN destined packets with [WMM mapped](https://datatracker.ietf.org/doc/html/rfc8325#section-4.3) classes to improve transmit prioritisation with 3rd party WiFi access points and switches, see the [wmm_mark_lan](#section-service) service configuration option.
-### 1. User rules
-The service will first attempt to classify new connections using rules specified by the user in the config file.
+_Users of layer-cake SQM should install the [layer_cake_ct](#layer_cake_ctqos) SQM script for setting DSCP marks on inbound packets, see [SQM Configuration](#sqm-configuration-)❗_
-These follow a similar syntax to the OpenWrt firewall config and can match upon source/destination ports and IPs, firewall zones etc.
+## User rules 📝
+You can create rules to classify new connections in the service [config file](#configuration-%EF%B8%8F).\
+These use a similar syntax to the OpenWrt firewall config and can match source and destination ports, addresses, ipsets, firewall zones etc.
-The rules support the use of nft sets, which could be dynamically updated from external sources such as dnsmasq.
+More information and examples can be found in the [rules section](#section-rule).
-### 2. Client class hinting
-The service can be configured to apply the DSCP mark supplied by a non WAN originating client.
+## Automatic classification 🪄
+Connections that don't match a rule will be automatically classified by the service using one of the below methods.
-This function ignores CS6 and CS7 classes to avoid abuse from inappropriately configed LAN clients such as IoT devices.
+### Client class adoption ✨
+The service can automatically adopt the DSCP mark supplied by a non-WAN client.\
+By default this ignores classes CS6 and CS7 to avoid abuse from clients such as IoT devices.
-### 3. Automatic classification
-Connections that do not match a user rule or client class hint will be automatically classified by the service to set their priority.
+### Bulk client detection for P2P traffic 🌎
+These connections are one of the largest causes of [Bufferbloat](https://en.wikipedia.org/wiki/Bufferbloat), as a result they are classified as **Low Effort (LE)** by default and therefore prioritised **below Best Effort (BE/DF/CS0)** traffic when using the layer-cake qdisc.
-#### Multi-connection client port detection for detecting P2P traffic
-These connections are classified as **Low Effort (LE**) by default and therefore prioritised **below Best Effort** traffic when using the layer-cake qdisc.
+### High Throughput service detection for Steam downloads, cloud storage etc 🚛
+Services such as Steam make use of parralel connections to maximise download bandwith, this can also cause bufferbloat and so these connections are classified as **High-Throughput (AF13)** by default and prioritised as follows by cake:
+ * **diffserv8**: prioritised **below Best Effort (BE/DF/CS0)** traffic and **above Low Effort (LE)** traffic
+ * **diffserv3/4**: prioritised **equal to Best Effort (BE/DF/CS0)** traffic
-#### Multi-threaded service detection for identifying high-throughput downloads from services such as Steam
-These connections are classified as **High-Throughput (AF13**) by default and therefore prioritised as follows by cake:
- * **diffserv3/4**: prioritised **equal to Best Effort (CS0**) traffic
- * **diffserv8**: prioritised **below Best Effort (CS0**) traffic, but **above Low Effort (LE**) traffic
+## Service architecture 🏗️
-## Service architecture
-
+The dscpclassify service uses the last 8 bits of the conntrack mark (0x000000**ff**), leaving the remaining bits for use by other applications.
-# Service installation
-1. To install the main dscpclassify service via command line you can use the following commands:
+
+
+# Service installation ⚙️
+To install dscpclassify service via command line you can use the following sets of commands.
+
+### dscpclassify
```
repo="https://raw.githubusercontent.com/jeverley/dscpclassify/main"
@@ -53,9 +57,9 @@ chmod +x "/etc/init.d/dscpclassify"
/etc/init.d/dscpclassify enable
/etc/init.d/dscpclassify start
```
-#### _Ingress DSCP marking requires the SQM queue setup script 'layer_cake_ct.qos' and the package 'kmod-sched-ctinfo'._
-2. To install the SQM setup script via command line you can use the following commands:
+### layer_cake_ct.qos
+#### _Ingress DSCP marking for SQM cake requires installation and [configuration](#sqm-configuration-) of 'layer_cake_ct.qos' and the package 'kmod-sched-ctinfo'❗_
```
repo="https://raw.githubusercontent.com/jeverley/dscpclassify/main"
@@ -64,38 +68,57 @@ opkg install kmod-sched-ctinfo
wget "$repo/usr/lib/sqm/layer_cake_ct.qos" -O "/usr/lib/sqm/layer_cake_ct.qos"
wget "$repo/usr/lib/sqm/layer_cake_ct.qos.help" -O "/usr/lib/sqm/layer_cake_ct.qos.help"
```
-# Configuration
+# Configuration ⚙️
The service configuration is located in '/etc/config/dscpclassify'.
-### A working default configuration is provided with the service which should work for most users.
-
-#### Global options
-|Option | Description | Type | Default|
-|--- | --- | --- | ---|
-|class_bulk | The class applied to threaded bulk clients | string | le|
-|class_high_throughput | The class applied to threaded high-throughput services | string | af13|
-|client_hints | Adopt the DSCP class supplied by a non-WAN client (this exludes CS6 and CS7 classes to avoid abuse) | boolean | 1|
-|threaded_client_detection | Automatically and classify threaded client connections (i.e. P2P) as bulk | boolean | 1|
-|threaded_service_detection | Automatically and classify threaded service connections (i.e. Windows Update/Steam downloads) as bulk | boolean | 1|
-|lan_device | Manually specify devices that the service should treat as LAN | list: string | |
-|lan_zone | Manually specify firewall zones that the service should treat as LAN | list: string | lan|
-|wan_device | Manually specify devices that the service should treat as WAN | list: string | |
-|wan_zone | Manually specify firewall zones that the service should treat as WAN | list: string | wan|
-|wmm | When enabled the service will mark LAN bound packets with DSCP values respective of WMM (RFC-8325) | boolean | 0|
-
-#### Advanced global options (not recommended for most users)
-|Option | Description | Type | Default|
-|--- | --- | --- | ---|
-|threaded_client_min_bytes | The total bytes before a threaded client port (i.e. P2P) is classified as bulk | uint | 10000|
-|threaded_client_min_connections | The number of established connections for a client port to be considered threaded | uint | 10|
-|threaded_service_min_bytes | The total bytes before a threaded service's connection is classed as high-throughput | uint | 1000000|
-|threaded_service_min_connections | The number of established connections for a service to be considered threaded | uint | 3|
-
-# User rules
-The user rules in '/etc/config/dscpclassify' use the same syntax as OpenWrt's firewall config, the 'class' option is used to specified the desired DSCP.
-The OpenWrt firewall syntax is outlined [here](https://openwrt.org/docs/guide-user/firewall/firewall_configuration).
-
-### Example user rule
+**A working default configuration is provided with the service which should work for most users.**
+
+### Section "service"
+|Name | Type | Required | Default | Description|
+|--- | --- | --- | --- | ---|
+|class_low_effort | string | no | le 1 | The default DSCP class applied to low effort connections |
+|class_high_throughput | string | no | af13 | The default DSCP class applied to high-throughput connections |
+|wmm_mark_lan | boolean | no | 0 | Mark packets going out of LAN interfaces with DSCP values respective of [WMM (RFC-8325)](https://datatracker.ietf.org/doc/html/rfc8325#section-4.3) |
+|**Advanced** | | | | _**The below options are typically only required on non-standard setups**_ |
+|_lan_zone_ | list | no | lan | Used to specify LAN firewall zones (lan/guest etc) |
+|_wan_zone_ | list | no | wan | Used to specify WAN firewall zones |
+|_lan_device_ | list | no | | Used to specify LAN network interfaces (L3 physical interface i.e. `br-lan`) |
+|_wan_device_ | list | no | | Used to specify WAN network interfaces (L3 physical interface) |
+
+_1. When running on older OpenWrt releases with kernels < 5.13 the service defaults to class CS1 for low effort connections_
+
+### Section "client_class_adoption"
+|Name | Type | Required | Default | Description|
+|--- | --- | --- | --- | ---|
+|enabled | boolean | no | 1 | Adopt the DSCP class supplied by a non-WAN client |
+|exclude_class | list | no | cs6, cs7 | Classes to ignore from client class adoption |
+|src_ip | list | no | | Include/Exclude source IPs for class adoption, preface excluded IPs with ! |
+
+### Section "bulk_client_detection"
+|Name | Type | Required | Default | Description|
+|--- | --- | --- | --- | ---|
+|enabled | boolean | no | 1 | Detect and classify bulk client connections (i.e. P2P)|
+|class | string | no | | Override the service level class_high_throughput setting |
+|**Advanced** | | | | _**The default configuration for the below should work for most users**_ |
+|_min_connections_ | number | no | 10 | Minimum established connections for a client port to be considered as bulk |
+|_min_bytes_ | number | no | 10000 | Minimum bytes before a client port is classified as bulk |
+
+### Section "high_throughput_service_detection"
+|Name | Type | Required | Default | Description|
+|--- | --- | --- | --- | ---|
+|enabled | boolean | no | 1 | Detect and classify high throughput service connections (i.e. Windows Update/Steam downloads)
+|class | string | no | | Override the service level class_high_throughput setting |
+|**Advanced** | | | | _**The default configuration for the below should work for most users**_ |
+|_min_connections_ | number | no | 3 | Minimum established connections for a service to be considered as high-throughput |
+|_min_bytes_ | number | no | 1000000 | Minimum bytes before the connection is classified as high-throughput |
+
+### Section "rule"
+The rule sections in `/etc/config/dscpclassify` use the same syntax as OpenWrt's firewal, the **class** option is used to specified the desired DSCP.\
+The OpenWrt fw4 rule syntax is outlined in the [OpenWrt Wiki](https://openwrt.org/docs/guide-user/firewall/firewall_configuration#rules), dscpclassify default rules can be viewed [here](https://github.com/jeverley/dscpclassify/blob/main/etc/config/dscpclassify)'.
+
+The rules support matching source/destination addresses in nft **sets**, these can be dynamically updated from external sources such as dnsmasq.
+
+#### Example user rule 📃
```
config rule
@@ -110,7 +133,30 @@ config rule
```
The counter option can be enabled to count the number of matched connections for a rule.
-# SQM configuration
+### Section "ipset"
+The ipset sections in `/etc/config/dscpclassify` use the same syntax as OpenWrt's firewall, they can be used in conjunction with rules for dynamically populated ip matching.\
+The OpenWrt fw4 ipset syntax is outlined in the [OpenWrt Wiki](https://openwrt.org/docs/guide-user/firewall/firewall_configuration#options_fw4), dscpclassify default rules can be viewed [here](https://github.com/jeverley/dscpclassify/blob/main/etc/config/dscpclassify).
+
+#### Example ipset and rule 📃
+
+```
+config ipset
+ option name 'xcloud'
+ option interval '1'
+ list entry '13.104.0.0/14' # Western Europe
+
+config rule
+ option name 'Xbox Cloud Gaming'
+ option proto 'udp'
+ option family 'ipv4'
+ list dest_ip '@xcloud'
+ list dest_port '1000-1150'
+ list dest_port '9002'
+ option class 'af41'
+```
+
+
+# SQM configuration 🚀
The **'layer_cake_ct.qos'** queue setup script must be selected for your wan device in SQM setup,
@@ -129,5 +175,5 @@ It is important that **Ignore DSCP** on ingress is **Allow** in SQM setup otherw
| **script** | **layer_cake_ct.qos** |
-
-
+
+
diff --git a/etc/config/dscpclassify b/etc/config/dscpclassify
index 7e44d65..745a899 100644
--- a/etc/config/dscpclassify
+++ b/etc/config/dscpclassify
@@ -1,10 +1,20 @@
-config global 'global'
- option class_bulk 'le'
- option class_high_throughput 'af13'
- option client_hints '1'
- option threaded_client_detection '1'
- option threaded_service_detection '1'
- option wmm '0'
+config service
+ option wmm_mark_lan '0'
+
+config client_class_adoption
+ option enabled '1'
+ list exclude_class 'cs6'
+ list exclude_class 'cs7'
+
+config bulk_client_detection
+ option enabled '1'
+ option min_bytes '10000'
+ option min_connections '10'
+
+config high_throughput_service_detection
+ option enabled '1'
+ option min_bytes '1000000'
+ option min_connections '3'
config ipset
option name 'xcloud'
@@ -30,22 +40,22 @@ config rule
option name 'DoH'
list proto 'tcp'
list proto 'udp'
- list dest_ip '8.8.8.8' # Google
- list dest_ip '8.8.4.4' # Google
- list dest_ip '1.1.1.1' # Cloudflare
- list dest_ip '1.0.0.1' # Cloudflare
- list dest_ip '9.9.9.9' # Quad9 Secured
+ list dest_ip '8.8.8.8' # Google
+ list dest_ip '8.8.4.4' # Google
+ list dest_ip '1.1.1.1' # Cloudflare
+ list dest_ip '1.0.0.1' # Cloudflare
+ list dest_ip '9.9.9.9' # Quad9 Secured
list dest_ip '149.112.112.112' # Quad9 Secured
- list dest_ip '9.9.9.11' # Quad9 Secured w/ECS
+ list dest_ip '9.9.9.11' # Quad9 Secured w/ECS
list dest_ip '149.112.112.11' # Quad9 Secured w/ECS
list dest_ip '94.140.14.0/24' # AdGuard
- list dest_ip '2001:4860:4860::8888' # Google
- list dest_ip '2001:4860:4860::8844' # Google
- list dest_ip '2606:4700:4700::1111' # Cloudflare
- list dest_ip '2606:4700:4700::1001' # Cloudflare
- list dest_ip '2620:fe::fe' # Quad9 Secured
- list dest_ip '2620:fe::9' # Quad9 Secured
- list dest_ip '2620:fe::11' # Quad9 Secured w/ECS
+ list dest_ip '2001:4860:4860::8888' # Google
+ list dest_ip '2001:4860:4860::8844' # Google
+ list dest_ip '2606:4700:4700::1111' # Cloudflare
+ list dest_ip '2606:4700:4700::1001' # Cloudflare
+ list dest_ip '2620:fe::fe' # Quad9 Secured
+ list dest_ip '2620:fe::9' # Quad9 Secured
+ list dest_ip '2620:fe::11' # Quad9 Secured w/ECS
list dest_ip '2620:fe::fe:11' # Quad9 Secured w/ECS
list dest_ip '2a10:50c0::ad1:ff' # AdGuard
list dest_ip '2a10:50c0::ad2:ff' # AdGuard
diff --git a/etc/dscpclassify.d/main.nft b/etc/dscpclassify.d/main.nft
index 71f09d5..ad0406d 100644
--- a/etc/dscpclassify.d/main.nft
+++ b/etc/dscpclassify.d/main.nft
@@ -67,9 +67,7 @@ table inet dscpclassify {
}
chain client_classify {
- ## Assess client DSCP mark for classification
- ip dscp != { cs0, cs6, cs7 } ip dscp vmap @dscp_ct
- ip6 dscp != { cs0, cs6, cs7 } ip6 dscp vmap @dscp_ct
+ ## Client class adoption rules are added here by the init script
}
chain dynamic_classify {
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 96a1fba..3d3383d 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -3,6 +3,7 @@
START=50
USE_PROCD=1
+DEBUG=1
# Dynamic content
DEBUG_FILE="/tmp/dscpclassify.debug"
@@ -15,42 +16,30 @@ MAIN="/etc/dscpclassify.d/main.nft"
VERDICTS="/etc/dscpclassify.d/verdicts.nft"
MAPS="/etc/dscpclassify.d/maps.nft"
-# Configuration defaults
-CLASS_BULK=le
-CLASS_HIGH_THROUGHPUT=af13
-CLIENT_HINTS=1
-DYNAMIC_CLASSIFY=1
-THREADED_CLIENT_DETECTION=1
-THREADED_CLIENT_MIN_BYTES=10000
-THREADED_CLIENT_MIN_CONNECTIONS=10
-THREADED_SERVICE_DETECTION=1
-THREADED_SERVICE_MIN_BYTES=1000000
-THREADED_SERVICE_MIN_CONNECTIONS=3
-WMM=0
-
-debug=1
-nft_result=""
-
+# Service constants
+SERVICE_NAME="dscpclassify"
+TABLE="$SERVICE_NAME"
+CONFIG="$SERVICE_NAME"
log() {
- logger -t dscpclassify -p "daemon.$1" "$2"
- case "$1" in
- info | warning | err) echo "$2" ;;
+ local level="$1" message="$2"
+ logger -t "$SERVICE_NAME" -p "daemon.${level}" "$message"
+ case "$level" in
+ info | warning | err) >&2 echo "$message" ;;
esac
}
create_debug_file() {
- # shellcheck disable=SC2154
{
- echo "dscpclassify ${action}: $(date)"
+ echo "{$SERVICE_NAME}${action:+ $action}: $(date)"
echo $'\n'"<--- ${PRE_INCLUDE} --->"
[ -f "$PRE_INCLUDE" ] && cat "$PRE_INCLUDE"
echo $'\n'"<--- ${POST_INCLUDE} --->"
[ -f "$POST_INCLUDE" ] && cat "$POST_INCLUDE"
echo $'\n'"<--- nft -f ${MAIN} --->"
- echo "$nft_result"
- echo $'\n'"<--- nft list table inet dscpclassify --->"
- nft list table inet dscpclassify 2>&1
+ echo "${nft_result:+$nft_result}"
+ echo $'\n'"<--- nft list table inet {$TABLE} --->"
+ nft list table inet "$TABLE" 2>&1
} > "$DEBUG_FILE"
}
@@ -63,7 +52,8 @@ delete_includes() {
}
destroy_table() {
- nft destroy table inet dscpclassify &>/dev/null
+ nft delete table inet "$TABLE" &>/dev/null
+ return 0
}
cleanup_service() {
@@ -77,41 +67,92 @@ cleanup_setup() {
delete_includes
}
+check_chain_exists() {
+ nft -t list chain inet "$TABLE" "$1" &>/dev/null
+}
+
+check_set_exists() {
+ nft -t list set inet "$TABLE" "$1" &>/dev/null
+}
+
+check_table_exists() {
+ nft -t list table inet "$TABLE" "$1" &>/dev/null
+}
+
+nft_compatibility() {
+ # Handle differences between nft versions
+ # Destroy is not supported in kernel versions < 6.3
+ # The latest kernels also return an error when destroy is used with non-existent sets
+ local command="$1" element
+
+ element=$(echo "$command" | awk '{print $NF}')
+ case "$command" in
+ "destroy chain "*) check_chain_exists "$element" || return 1 ;;
+ "destroy set "*) check_set_exists "$element" || return 1 ;;
+ "destroy table "*) check_table_exists "$element" || return 1 ;;
+ esac
+ echo "$command" | sed "s/^destroy /$destroy_action /"
+ return 0
+}
+
pre_include() {
- echo "$1" >>"$PRE_INCLUDE"
+ local command
+ command=$(nft_compatibility "$1") || return 0
+ echo "$command" >>"$PRE_INCLUDE"
}
post_include() {
- echo "$1" >>"$POST_INCLUDE"
+ local command
+ command=$(nft_compatibility "$1") || return 0
+ echo "$command" >>"$POST_INCLUDE"
}
-config_foreach_reverse() {
- local function="$1" type="$2"
- local list
- # shellcheck disable=SC2329
- list_append() {
- list="$list"$'\n'"$1"
- }
- config_foreach list_append "$type"
- list=$(echo "$list" | sort -r)
-
+config_foreach_reverse() {
+ local ___function="$1"
+ local ___type="$2"
shift 2
- for config in $list; do
- "$function" "$config" "$@"
+
+ for section in $(config_foreach echo "$___type" | sort -r); do
+ "$___function" "$section" "$@"
+ done
+}
+
+# config_get_exclusive_section
+# config_get_exclusive_section
+config_get_exclusive_section() {
+ local type="${2:-$1}" variable="${2:+$1}"
+ local section
+
+ case "${type}${variable}" in
+ *[!A-Za-z0-9_]*) return 1 ;;
+ esac
+
+ for _section in $(config_foreach echo "$type"); do
+ [ -n "$section" ] && {
+ log warning "Duplicate ${type} config section ignored"
+ break
+ }
+ section="$_section"
done
+
+ [ -n "$variable" ] && {
+ eval "${variable}=\${section}"
+ return 0
+ }
+ echo "$section"
}
convert_duration_to_seconds() {
+ local duration="$1"
local seconds
- duration="$(echo "$1" | sed -e 's/\([dhms]\)/\1 /g')"
- for i in $duration; do
- case "$i" in
- *d) seconds=$((seconds + ${i::-1} * 86400)) || return 1 ;;
- *h) seconds=$((seconds + ${i::-1} * 3600)) || return 1 ;;
- *m) seconds=$((seconds + ${i::-1} * 60)) || return 1 ;;
- *s) seconds=$((seconds + ${i::-1})) || return 1 ;;
+ for component in $(echo "$duration" | sed -e 's/\([dhms]\)/\1 /g'); do
+ case "$component" in
+ *d) seconds=$((seconds + ${component::-1} * 86400)) || return 1 ;;
+ *h) seconds=$((seconds + ${component::-1} * 3600)) || return 1 ;;
+ *m) seconds=$((seconds + ${component::-1} * 60)) || return 1 ;;
+ *s) seconds=$((seconds + ${component::-1})) || return 1 ;;
*) return 1 ;;
esac
done
@@ -134,18 +175,55 @@ dscp_class() {
}
format_list() {
- local items="$1" delimiter="$2" encapsulator="$3"
+ local items="$1" delimiter="$2" encapsulator="$3" wrapper="$4"
+ local list
- echo "$items" | tr '\n' ' ' | sed -e "s/^\s*/${encapsulator}/" -e "s/\s*$/${encapsulator}/" -e "s/\([^.]\)\s\+\([^.]\)/\1${encapsulator}${delimiter}${encapsulator}\2/g"
+ list=$(echo "$items" | tr '\n' ' ' | sed -e "s/^\s*/${encapsulator}/" -e "s/\s*$/${encapsulator}/" -e "s/\s\+/${encapsulator}${delimiter}${encapsulator}/g")
+ case ${#wrapper} in
+ 0) echo "$list" ;;
+ 2) echo "${wrapper:0:1} ${list} ${wrapper:1:1}" ;;
+ *) return 1 ;;
+ esac
}
-fw_zone_devices() {
- local dev
+nft_element_list() {
+ format_list "$1" ", " "" "{}"
+}
- dev="$(fw4 -q zone "$1" | sort -u)"
- [ -n "$dev" ] || return 1
+nft_interface_list() {
+ format_list "$1" ", " "\"" "{}"
+}
- echo "$dev"
+nft_flag_list() {
+ format_list "$1" ", "
+}
+
+nft_type_list() {
+ format_list "$1" " . "
+}
+
+fw_zone_interfaces() {
+ local interfaces
+
+ interfaces="$(fw4 -q zone "$1" | sort -u)"
+ [ -n "$interfaces" ] || return 1
+ echo "$interfaces"
+}
+
+check_minimum_kernel_release() {
+ local minimum_release="$1"
+ local current_release current_major current_minor minimum_major minimum_minor
+
+ minimum_major=$(echo "$minimum_release" | awk -F '.' '{print $1}')
+ minimum_minor=$(echo "$minimum_release" | awk -F '.' '{print $2}')
+ current_release=$(uname -r)
+ current_major=$(echo "$current_release" | awk -F '.' '{print $1}')
+ current_minor=$(echo "$current_release" | awk -F '.' '{print $2}')
+
+ if [ "$current_major" -gt "$minimum_major" ] || { [ "$current_major" = "$minimum_major" ] && [ "$current_minor" -ge "${minimum_minor:-0}" ]; }; then
+ return 0
+ fi 2>/dev/null
+ return 1
}
check_duration() {
@@ -174,12 +252,12 @@ check_set_name() {
log warning "Set is missing the name option"
return 1
;;
- threaded_clients | threaded_clients6 | threaded_services | threaded_services6)
- log warning "Sets cannot overwrite built-in dscpclassify sets"
+ bulk_clients | bulk_clients6 | high_throughput_services | high_throughput_services6)
+ log warning "Sets cannot overwrite built-in $TABLE sets"
return 1
;;
- tc_detect | tc_detect6 | tc_orig_bulk | tc_orig_bulk6 | tc_reply_bulk | tc_reply_bulk6 | ts_detect | ts_detect6)
- log warning "Sets cannot overwrite built-in dscpclassify meter sets"
+ bulk_client_detect | bulk_client_detect6 | bulk_client_orig_classify | bulk_client_orig_classify6 | bulk_client_reply_classify | bulk_client_reply_classify6 | high_throughput_service_detect | high_throughput_service_detect6)
+ log warning "Sets cannot overwrite built-in $TABLE meter sets"
return 1
;;
esac
@@ -189,29 +267,25 @@ check_set_name() {
check_set_size() {
[ -n "$1" ] || return 0
- if ! [ "$1" -ge 1 ] 2>/dev/null || ! [ "$1" -le 65535 ] 2>/dev/null; then
+ if ! [ "$1" -ge 1 ] || ! [ "$1" -le 65535 ]; then
log warning "Set contains an invalid maxelem option"
return 1
- fi
+ fi 2>/dev/null
return 0
}
-check_set_exists() {
- nft -t list set inet dscpclassify "$1" &>/dev/null
-}
-
check_set_against_existing() {
local name="$1" type="$2" comment="$3" size="$4" flags="$5" timeout="$6"
local existing_set existing_type
- existing_set=$(nft -t -j list set inet dscpclassify "$name" 2>/dev/null) || return 2
+ existing_set=$(nft -t -j list set inet "$TABLE" "$name" 2>/dev/null) || return 2
- type="$(echo "$type" | sed 's/ \+\. \+/ /g')"
+ type="$(echo "$type" | sed 's/\s\+\.\s\+/\n/g')"
existing_type="$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.type")"
if [ -n "$existing_type" ]; then
[ "$existing_type" = "$type" ] || return 1
else
- existing_type="$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.type[*]" | tr '\n' ' ' | sed 's/ *$//')"
+ existing_type="$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.type[*]")"
[ "$existing_type" = "$type" ] || return 1
fi
@@ -219,8 +293,8 @@ check_set_against_existing() {
[ "$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.size")" = "$size" ] || return 1
- flags="$(echo "$flags" | sed 's/, \+/ /g')"
- [ "$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.flags[*]" | tr '\n' ' ' | sed 's/ *$//')" = "$flags" ] || return 1
+ flags="$(echo "$flags" | sed 's/,\s\+/\n/g' | sort)"
+ [ "$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.flags[*]" | sort)" = "$flags" ] || return 1
timeout="$(convert_duration_to_seconds "$timeout")"
[ "$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.timeout")" = "$timeout" ] || return 1
@@ -312,7 +386,7 @@ parse_set_flags() {
flags="$flags timeout"
}
- [ -n "$flags" ] && flags="$(format_list "$flags" ", ")"
+ [ -n "$flags" ] && flags=$(nft_flag_list "$flags")
return 0
}
@@ -350,87 +424,91 @@ parse_set_type() {
;;
esac
done
- type=$(format_list "$type" " . ")
+ type=$(nft_type_list "$type")
return 0
}
create_user_set() {
+ local section="$1"
local comment entry element enabled family flags match size name timeout type
local flag_constant flag_interval flag_timeout auto_merge
+ local error=0
- config_get_bool enabled "$1" enabled 1
+ config_get_bool enabled "$section" enabled 1
[ "$enabled" = 1 ] || return 0
- config_get comment "$1" comment
- config_get name "$1" name
- config_get family "$1" family ipv4
- config_get match "$1" match
- config_get type "$1" type # allows user to explicity specify the nft set type
- config_get size "$1" maxelem
- config_get timeout "$1" timeout
+ config_get comment "$section" comment
+ config_get name "$section" name
+ config_get family "$section" family ipv4
+ config_get match "$section" match
+ config_get type "$section" type # allows user to explicity specify the nft set type
+ config_get size "$section" maxelem
+ config_get timeout "$section" timeout
- config_get_bool flag_constant "$1" constant
- config_get_bool flag_interval "$1" interval
+ config_get_bool flag_constant "$section" constant
+ config_get_bool flag_interval "$section" interval
- config_get entry "$1" entry
- config_get element "$1" element # deprecate for naming consistency with fw4 (entry)
+ config_get entry "$section" entry
+ config_get element "$section" element # deprecate for naming consistency with fw4 (entry)
[ -n "$element" ] && log warning "The user set 'element' option is being deprecated in favour of 'entry' for consistency with fw4"
- check_set_name "$name" || return 1
- check_set_size "$size" || return 1
+ check_set_name "$name" || error=1
+ check_set_size "$size" || error=1
check_family "$family" || {
log warning "Set contains an invalid family"
- return 1
+ error=1
}
- parse_set_type || return 1
- parse_set_timeout || return 1
- parse_set_flags || return 1
+ parse_set_type || error=1
+ parse_set_timeout || error=1
+ parse_set_flags || error=1
+ [ "$error" = 0 ] || return 1
check_set_against_existing "$name" "$type" "$comment" "$size" "$flags" "$timeout" || {
- [ "$?" = 1 ] && post_include "destroy set inet dscpclassify $name"
- post_include "add set inet dscpclassify $name { type $type; ${timeout:+timeout $timeout;} ${size:+size $size;} ${flags:+flags $flags;} ${auto_merge:+auto-merge;} ${comment:+comment \"$comment\";} }"
+ [ "$?" = 1 ] && post_include "destroy set inet $TABLE $name"
+ post_include "add set inet $TABLE $name { type $type; ${timeout:+timeout $timeout;} ${size:+size $size;} ${flags:+flags $flags;} ${auto_merge:+auto-merge;} ${comment:+comment \"$comment\";} }"
}
- [ -n "$entry$element" ] && post_include "add element inet dscpclassify $name { $(format_list "$entry $element" ", ") }"
+ [ -n "$entry$element" ] && post_include "add element inet $TABLE $name $(nft_element_list "$entry $element")"
return 0
}
rule_l4proto() {
[ -n "$1" ] || return 0
- l4proto="meta l4proto { $(format_list "$1" ", ") }"
+ l4proto="meta l4proto $(nft_element_list "$1")"
}
rule_nfproto() {
[ -n "$1" ] || return 0
- nfproto="meta nfproto { $(format_list "$1" ", ") }"
+ nfproto="meta nfproto $(nft_element_list "$1")"
}
rule_oifname() {
[ -n "$1" ] || return 0
- oifname="oifname { $(format_list "$1" ", " "\"") }"
+ oifname="oifname $(nft_interface_list "$1")"
}
rule_iifname() {
[ -n "$1" ] || return 0
- iifname="iifname { $(format_list "$1" ", " "\"") }"
+ iifname="iifname $(nft_interface_list "$1")"
}
rule_zone() {
- local device
+ local direction="$1" zone="$2"
+ local interfaces
- [ -n "$2" ] || return 0
+ [ -n "$zone" ] || return 0
- device="$(fw_zone_devices "$2")" || {
- log warning "Rule contains an invalid $1 zone"
+ interfaces="$(fw_zone_interfaces "$zone")" || {
+ log warning "Rule contains an invalid ${direction} zone"
return 1
}
- case "$1" in
- src) rule_iifname "$device" ;;
- dest) rule_oifname "$device" ;;
+ case "$direction" in
+ src) rule_iifname "$interfaces" ;;
+ dest) rule_oifname "$interfaces" ;;
*)
log err "Invalid direction for zone function"
return 1
@@ -439,9 +517,10 @@ rule_zone() {
}
rule_port() {
+ local direction="$1" ports="$2" protocol="$3"
local port port_negate rule xport
- case "$1" in
+ case "$direction" in
src) xport="sport" ;;
dest) xport="dport" ;;
*)
@@ -450,30 +529,32 @@ rule_port() {
;;
esac
- [ -n "$2" ] || return 0
+ [ -n "$ports" ] || return 0
- parse_rule_ports "$2" || {
- log warning "Rule contains an invalid $1_port"
+ check_port_proto "$protocol" || {
+ log warning "Rules cannot combine a ${direction}_port with protocols other than 'tcp' or 'udp'"
return 1
}
- check_port_proto "$3" || {
- log warning "Rules cannot combine a $1_port with protocols other than 'tcp' or 'udp'"
+
+ parse_rule_ports "$ports" || {
+ log warning "Rule contains an invalid ${direction}_port"
return 1
}
- [ -n "$port" ] && rule="th $xport { $(format_list "$port" ", ") }"
- [ -n "$port_negate" ] && rule="$rule th $xport != { $(format_list "$port_negate" ", ") }"
+ [ -n "$port" ] && rule="th $xport $(nft_element_list "$port")"
+ [ -n "$port_negate" ] && rule="$rule th $xport != $(nft_element_list "$port_negate")"
eval "$xport"='$rule'
return 0
}
rule_addr() {
+ local direction="$1" addresses="$2" family="$3"
local rule rule6 xaddr
local ipv4 ipv6 ipset
local ipv4_negate ipv6_negate ipset_negate
- case "$1" in
+ case "$direction" in
src) xaddr="saddr" ;;
dest) xaddr="daddr" ;;
*)
@@ -482,40 +563,41 @@ rule_addr() {
;;
esac
- [ -n "$2" ] || return 0
+ [ -n "$addresses" ] || return 0
- if [ -n "$3" ] && ! check_family "$3"; then
+ if [ -n "$family" ] && ! check_family "$family"; then
log warning "Rule contains an invalid family"
return 1
fi
- parse_rule_ips "$2" || {
- log warning "Rule contains an invalid $1_ip"
+
+ parse_rule_ips "$addresses" || {
+ log warning "Rule contains an invalid ${direction}_ip"
return 1
}
if [ -n "$ipset$ipset_negate" ] && [ -n "$ipv4$ipv6$ipv4_negate$ipv6_negate" ]; then
- log warning "Rules must not mix IP addresses and sets in the $1_ip option"
+ log warning "Rules must not mix IP addresses and sets in the ${direction}_ip option"
return 1
fi
- if [ -n "$ipv4$ipv4_negate" ] && [ "$3" = "ipv6" ]; then
- log warning "Rules cannot combine an ipv4 $1_ip with the 'ipv6' family option"
+ if [ -n "$ipv4$ipv4_negate" ] && [ "$family" = "ipv6" ]; then
+ log warning "Rules cannot combine an ipv4 ${direction}_ip with the 'ipv6' family option"
return 1
fi
- if [ -n "$ipv6$ipv6_negate" ] && [ "$3" = "ipv4" ]; then
- log warning "Rules cannot combine an ipv6 $1_ip with the 'ipv4' family option"
+ if [ -n "$ipv6$ipv6_negate" ] && [ "$family" = "ipv4" ]; then
+ log warning "Rules cannot combine an ipv6 ${direction}_ip with the 'ipv4' family option"
return 1
fi
if [ "$(echo "$ipset" | wc -w)" -gt 1 ] || [ "$(echo "$ipset_negate" | wc -w)" -gt 1 ]; then
- log warning "Rules must not contain more than one set for the $1_ip option"
+ log warning "Rules must not contain more than one set for the ${direction}_ip option"
return 1
fi
- [ -n "$ipv4" ] && rule="ip $xaddr { $(format_list "$ipv4" ", ") }"
- [ -n "$ipv4_negate" ] && rule="$rule ip $xaddr != { $(format_list "$ipv4_negate" ", ") }"
+ [ -n "$ipv4" ] && rule="ip $xaddr $(nft_element_list "$ipv4")"
+ [ -n "$ipv4_negate" ] && rule="$rule ip $xaddr != $(nft_element_list "$ipv4_negate")"
- [ -n "$ipv6" ] && rule6="ip6 $xaddr { $(format_list "$ipv6" ", ") }"
- [ -n "$ipv6_negate" ] && rule6="$rule6 ip6 $xaddr != { $(format_list "$ipv6_negate" ", ") }"
+ [ -n "$ipv6" ] && rule6="ip6 $xaddr $(nft_element_list "$ipv6")"
+ [ -n "$ipv6_negate" ] && rule6="$rule6 ip6 $xaddr != $(nft_element_list "$ipv6_negate")"
- [ -n "$ipset$ipset_negate" ] && case "$3" in
+ [ -n "$ipset$ipset_negate" ] && case "$family" in
ipv4)
[ -n "$ipset" ] && rule="$rule ip $xaddr $ipset"
[ -n "$ipset_negate" ] && rule="$rule ip $xaddr != $ipset_negate"
@@ -525,7 +607,7 @@ rule_addr() {
[ -n "$ipset_negate" ] && rule6="$rule6 ip6 $xaddr != $ipset_negate"
;;
*)
- log warning "Rules must contain the family option when a set is present in the $1_ip option"
+ log warning "Rules must contain the family option when a set is present in the ${direction}_ip option"
return 1
;;
esac
@@ -536,18 +618,19 @@ rule_addr() {
}
rule_device() {
- [ -n "$1" ] || return 0
+ local device="$1" direction="$2"
+ [ -n "$device" ] || return 0
- [ -n "$2" ] || {
- log warning "Rules must use the device and direction options in conjunction"
+ [ -n "$direction" ] || {
+ log warning "Rules must use the options 'device' and 'direction' in conjunction"
return 1
}
- case "$2" in
- in) rule_iifname "$1" ;;
- out) rule_oifname "$1" ;;
+ case "$direction" in
+ in) rule_iifname "$device" ;;
+ out) rule_oifname "$device" ;;
*)
- log warning "The direction rule option must contain either 'in' or 'out'"
+ log warning "The rule option 'direction' must contain either 'in' or 'out'"
return 1
;;
esac
@@ -557,248 +640,310 @@ rule_verdict() {
local class="$1"
[ -n "$class" ] || {
- log warning "Rule is missing the DSCP class option"
+ log warning "Rule is missing the DSCP 'class' option"
return 1
}
class="$(dscp_class "$class")" || {
- log warning "Rule contains an invalid DSCP class"
+ log warning "Rule option 'class' contains an invalid DSCP value"
return 1
}
verdict="goto ct_set_${class}"
}
create_user_rule() {
+ local section="$1"
local enabled family proto direction device dest dest_ip dest_port src src_ip src_port counter class name
local nfproto l4proto oifname daddr daddr6 dport iifname saddr saddr6 sport verdict
+ local error=0
- config_get_bool enabled "$1" enabled 1
+ config_get_bool enabled "$section" enabled 1
[ "$enabled" = 1 ] || return 0
- config_get family "$1" family
- config_get proto "$1" proto
- config_get device "$1" device
- config_get direction "$1" direction
- config_get dest "$1" dest
- config_get dest_ip "$1" dest_ip
- config_get dest_port "$1" dest_port
- config_get src "$1" src
- config_get src_ip "$1" src_ip
- config_get src_port "$1" src_port
- config_get_bool counter "$1" counter
- config_get class "$1" class
- config_get name "$1" name
-
- rule_nfproto "$family" || return 1
- rule_l4proto "$proto" || return 1
- rule_zone dest "$dest" || return 1
- rule_addr dest "$dest_ip" "$family" || return 1
- rule_port dest "$dest_port" "$proto" || return 1
- rule_zone src "$src" || return 1
- rule_addr src "$src_ip" "$family" || return 1
- rule_port src "$src_port" "$proto" || return 1
- rule_device "$device" "$direction" || return 1
- rule_verdict "$class" || return 1
+ config_get family "$section" family
+ config_get proto "$section" proto
+ config_get device "$section" device # L3 physical interface
+ config_get direction "$section" direction
+ config_get dest "$section" dest
+ config_get dest_ip "$section" dest_ip
+ config_get dest_port "$section" dest_port
+ config_get src "$section" src
+ config_get src_ip "$section" src_ip
+ config_get src_port "$section" src_port
+ config_get_bool counter "$section" counter
+ config_get class "$section" class
+ config_get name "$section" name
+
+ rule_nfproto "$family" || error=1
+ rule_l4proto "$proto" || error=1
+ rule_zone dest "$dest" || error=1
+ rule_addr dest "$dest_ip" "$family" || error=1
+ rule_port dest "$dest_port" "$proto" || error=1
+ rule_zone src "$src" || error=1
+ rule_addr src "$src_ip" "$family" || error=1
+ rule_port src "$src_port" "$proto" || error=1
+ rule_device "$device" "$direction" || error=1
+ rule_verdict "$class" || error=1
+ [ "$error" = 0 ] || return 1
[ -z "$daddr$saddr$daddr6$saddr6" ] && {
- post_include "insert rule inet dscpclassify rule_classify $nfproto $l4proto $oifname $dport $iifname $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
+ post_include "insert rule inet $TABLE rule_classify $nfproto $l4proto $oifname $dport $iifname $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
return 0
}
[ -n "$daddr$saddr" ] && {
- post_include "insert rule inet dscpclassify rule_classify $nfproto $l4proto $oifname $daddr $dport $iifname $saddr $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
+ post_include "insert rule inet $TABLE rule_classify $nfproto $l4proto $oifname $daddr $dport $iifname $saddr $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
}
[ -n "$daddr6$saddr6" ] && {
- post_include "insert rule inet dscpclassify rule_classify $nfproto $l4proto $oifname $daddr6 $dport $iifname $saddr6 $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
+ post_include "insert rule inet $TABLE rule_classify $nfproto $l4proto $oifname $daddr6 $dport $iifname $saddr6 $sport ${counter:+counter} $verdict ${name:+comment \"$name\"}"
}
return 0
}
-create_client_classify_jump() {
- local client_hints
+destroy_client_class_adoption_rules() {
+ post_include "destroy chain inet $TABLE client_classify"
+}
+
+create_client_class_adoption_rules() {
+ local enabled=1
+ local exclude_class_defaults="cs6 cs7"
+ local exclude_class src_ip
+ local saddr saddr6
+
+ config_get_bool enabled "$client_class_adoption_config" enabled "$enabled"
+ [ "$enabled" = 1 ] || {
+ destroy_client_class_adoption_rules
+ return 0
+ }
+
+ config_get exclude_class "$client_class_adoption_config" exclude_class "$exclude_class_defaults"
+ for class in $exclude_class; do
+ class="$(dscp_class "$class")" || {
+ log err "The client_class_adoption config option 'exclude_class' contains an invalid DSCP class"
+ return 1
+ }
+ case "$class" in
+ le) class=lephb ;; # RFC-8622
+ cs0) continue ;;
+ esac
+ exclude_class="${exclude_class:+$exclude_class }\$${class}"
+ done
+
+ # Create exluded classes list, cs0 is always excluded
+ exclude_class="$(nft_element_list "cs0 $exclude_class")"
+
+ config_get src_ip "$section" src_ip
+ rule_addr src "$src_ip" || {
+ log err "The client_class_adoption config is invalid"
+ return 1
+ }
- config_get_bool client_hints global client_hints $CLIENT_HINTS
- [ "$client_hints" = 1 ] || return 0
+ # Create client class adoption rules
+ post_include "add rule inet $TABLE client_classify $saddr ip dscp != $exclude_class ip dscp vmap @dscp_ct"
+ post_include "add rule inet $TABLE client_classify $saddr6 ip6 dscp != $exclude_class ip6 dscp vmap @dscp_ct"
- post_include "add rule inet dscpclassify input ct mark & \$ct_dynamic != 0 ct direction original iifname != \$wan jump client_classify"
- post_include "add rule inet dscpclassify postrouting ct mark & \$ct_dynamic != 0 ct direction original iifname != \$wan jump client_classify"
+ # Create jump from input and postrouting chains
+ post_include "add rule inet $TABLE input ct mark & \$ct_dynamic != 0 ct direction original iifname != \$wan jump client_classify"
+ post_include "add rule inet $TABLE postrouting ct mark & \$ct_dynamic != 0 ct direction original iifname != \$wan jump client_classify"
}
-create_dynamic_classify_jump() {
- local dynamic_classify
+destroy_dynamic_classify_rules() {
+ post_include "destroy chain inet $TABLE dynamic_classify"
+ post_include "destroy chain inet $TABLE dynamic_classify_reply"
+ post_include "destroy chain inet $TABLE established_connection"
+}
+
+create_dynamic_classify_rules() {
+ local bulk_client_detection=1
+ local high_throughput_service_detection=1
- config_get_bool dynamic_classify global dynamic_classify $DYNAMIC_CLASSIFY
- [ "$dynamic_classify" = 1 ] || return 0
+ config_get_bool bulk_client_detection "$bulk_client_detection_config" enabled "$bulk_client_detection"
+ config_get_bool high_throughput_service_detection "$high_throughput_service_detection_config" enabled "$high_throughput_service_detection"
+ if [ "$bulk_client_detection" != 1 ] && [ "$high_throughput_service_detection" != 1 ]; then
+ destroy_dynamic_classify_rules
+ return 0
+ fi
- post_include "add rule inet dscpclassify input ct mark & (\$ct_dynamic | \$ct_dscp) == \$ct_dynamic jump dynamic_classify"
- post_include "add rule inet dscpclassify postrouting ct mark & (\$ct_dynamic | \$ct_dscp) == \$ct_dynamic jump dynamic_classify"
+ post_include "add rule inet $TABLE input ct mark & (\$ct_dynamic | \$ct_dscp) == \$ct_dynamic jump dynamic_classify"
+ post_include "add rule inet $TABLE postrouting ct mark & (\$ct_dynamic | \$ct_dscp) == \$ct_dynamic jump dynamic_classify"
}
-destroy_threaded_client_rules() {
- post_include "destroy chain inet dscpclassify threaded_client"
- post_include "destroy chain inet dscpclassify threaded_client_reply"
+destroy_bulk_client_rules() {
+ post_include "destroy chain inet $TABLE bulk_client"
+ post_include "destroy chain inet $TABLE bulk_client_reply"
- check_set_exists threaded_clients && post_include "destroy set inet dscpclassify threaded_clients"
- check_set_exists threaded_clients6 && post_include "destroy set inet dscpclassify threaded_clients6"
+ post_include "destroy set inet $TABLE bulk_clients"
+ post_include "destroy set inet $TABLE bulk_clients6"
}
-create_threaded_client_rules() {
- local class_bulk dynamic_classify threaded_client_detection threaded_client_min_bytes threaded_client_min_connections
+create_bulk_client_rules() {
+ local class="$class_low_effort"
+ local enabled=1
+ local min_bytes=10000
+ local min_connections=10
- config_get_bool dynamic_classify global dynamic_classify $DYNAMIC_CLASSIFY
- config_get_bool threaded_client_detection global threaded_client_detection $THREADED_CLIENT_DETECTION
- if [ "$dynamic_classify" != 1 ] || [ "$threaded_client_detection" != 1 ]; then
- destroy_threaded_client_rules
+ config_get_bool enabled "$bulk_client_detection_config" enabled "$enabled"
+ [ "$enabled" = 1 ] || {
+ destroy_bulk_client_rules
return 0
- fi
+ }
- config_get threaded_client_min_connections global threaded_client_min_connections $THREADED_CLIENT_MIN_CONNECTIONS
- if ! check_uint "$threaded_client_min_connections" || [ "$threaded_client_min_connections" -lt 2 ]; then
- log err "Global option threaded_client_min_connections contains an invalid value"
+ config_get min_bytes "$bulk_client_detection_config" min_bytes "$min_bytes"
+ if ! check_uint "$min_bytes" || [ "$min_bytes" = 0 ]; then
+ log err "bulk_client_detection config option 'min_bytes' contains an invalid value"
return 1
fi
- config_get threaded_client_min_bytes global threaded_client_min_bytes $THREADED_CLIENT_MIN_BYTES
- if ! check_uint "$threaded_client_min_bytes" || [ "$threaded_client_min_bytes" = 0 ]; then
- log err "Global option threaded_client_min_bytes contains an invalid value"
+ config_get min_connections "$bulk_client_detection_config" min_connections "$min_connections"
+ if ! check_uint "$min_connections" || [ "$min_connections" -lt 2 ]; then
+ log err "bulk_client_detection config option 'min_connections' contains an invalid value"
return 1
fi
- config_get class_bulk global class_bulk $CLASS_BULK
- class_bulk="$(dscp_class "$class_bulk")" || {
- log err "Global option class_bulk contains an invalid DSCP class"
+ config_get class "$bulk_client_detection_config" class "$class"
+ class="$(dscp_class "$class")" || {
+ log err "bulk_client_detection config option 'class' contains an invalid DSCP class"
return 1
}
- case "$class_bulk" in
- le) class_bulk=lephb ;; # RFC-8622
- cs0) destroy_threaded_client_rules; return 0 ;; # Skip rule creation as CS0 is the default
+ case "$class" in
+ le) class=lephb ;; # RFC-8622
+ cs0)
+ destroy_bulk_client_rules
+ log warning "Disabling threaded client detection as its configured class CS0/DF/BE is the default packet class"
+ return 0
+ ;;
esac
# Create sets for matching threaded clients
- check_set_exists threaded_clients || post_include "add set inet dscpclassify threaded_clients { type ipv4_addr . inet_service . inet_proto; flags timeout; }"
- check_set_exists threaded_clients6 || post_include "add set inet dscpclassify threaded_clients6 { type ipv6_addr . inet_service . inet_proto; flags timeout; }"
+ check_set_exists bulk_clients || post_include "add set inet $TABLE bulk_clients { type ipv4_addr . inet_service . inet_proto; flags timeout; }"
+ check_set_exists bulk_clients6 || post_include "add set inet $TABLE bulk_clients6 { type ipv6_addr . inet_service . inet_proto; flags timeout; }"
# Create threaded client detection rules
- post_include "add rule inet dscpclassify established_connection meter tc_detect { ip daddr . th dport . meta l4proto timeout 5s limit rate over $((threaded_client_min_connections - 1))/minute } add @threaded_clients { ip daddr . th dport . meta l4proto timeout 30s }"
- post_include "add rule inet dscpclassify established_connection meter tc_detect6 { ip6 daddr . th dport . meta l4proto timeout 5s limit rate over $((threaded_client_min_connections - 1))/minute } add @threaded_clients6 { ip6 daddr . th dport . meta l4proto timeout 30s }"
+ post_include "add rule inet $TABLE established_connection meter bulk_client_detect { ip daddr . th dport . meta l4proto timeout 5s limit rate over $((min_connections - 1))/minute } add @bulk_clients { ip daddr . th dport . meta l4proto timeout 30s }"
+ post_include "add rule inet $TABLE established_connection meter bulk_client_detect6 { ip6 daddr . th dport . meta l4proto timeout 5s limit rate over $((min_connections - 1))/minute } add @bulk_clients6 { ip6 daddr . th dport . meta l4proto timeout 30s }"
# Create threaded client classification rule chains
- post_include "add chain inet dscpclassify threaded_client"
- post_include "add rule inet dscpclassify threaded_client meter tc_orig_bulk { ip saddr . th sport . meta l4proto timeout 5m limit rate over $((threaded_client_min_bytes - 1)) bytes/hour } update @threaded_clients { ip saddr . th sport . meta l4proto timeout 5m } ct mark set ct mark | \$${class_bulk} return"
- post_include "add rule inet dscpclassify threaded_client meter tc_orig_bulk6 { ip6 saddr . th sport . meta l4proto timeout 5m limit rate over $((threaded_client_min_bytes - 1)) bytes/hour } update @threaded_clients6 { ip6 saddr . th sport . meta l4proto timeout 5m } ct mark set ct mark | \$${class_bulk} return"
+ post_include "add chain inet $TABLE bulk_client"
+ post_include "add rule inet $TABLE bulk_client meter bulk_client_orig_classify { ip saddr . th sport . meta l4proto timeout 5m limit rate over $((min_bytes - 1)) bytes/hour } update @bulk_clients { ip saddr . th sport . meta l4proto timeout 5m } ct mark set ct mark | \$${class} return"
+ post_include "add rule inet $TABLE bulk_client meter bulk_client_orig_classify6 { ip6 saddr . th sport . meta l4proto timeout 5m limit rate over $((min_bytes - 1)) bytes/hour } update @bulk_clients6 { ip6 saddr . th sport . meta l4proto timeout 5m } ct mark set ct mark | \$${class} return"
- post_include "add chain inet dscpclassify threaded_client_reply"
- post_include "add rule inet dscpclassify threaded_client_reply meter tc_reply_bulk { ip daddr . th dport . meta l4proto timeout 5m limit rate over $((threaded_client_min_bytes - 1)) bytes/hour } update @threaded_clients { ip daddr . th dport . meta l4proto timeout 5m } ct mark set ct mark | \$${class_bulk} return"
- post_include "add rule inet dscpclassify threaded_client_reply meter tc_reply_bulk6 { ip6 daddr . th dport . meta l4proto timeout 5m limit rate over $((threaded_client_min_bytes - 1)) bytes/hour } update @threaded_clients6 { ip6 daddr . th dport . meta l4proto timeout 5m } ct mark set ct mark | \$${class_bulk} return"
+ post_include "add chain inet $TABLE bulk_client_reply"
+ post_include "add rule inet $TABLE bulk_client_reply meter bulk_client_reply_classify { ip daddr . th dport . meta l4proto timeout 5m limit rate over $((min_bytes - 1)) bytes/hour } update @bulk_clients { ip daddr . th dport . meta l4proto timeout 5m } ct mark set ct mark | \$${class} return"
+ post_include "add rule inet $TABLE bulk_client_reply meter bulk_client_reply_classify6 { ip6 daddr . th dport . meta l4proto timeout 5m limit rate over $((min_bytes - 1)) bytes/hour } update @bulk_clients6 { ip6 daddr . th dport . meta l4proto timeout 5m } ct mark set ct mark | \$${class} return"
# Create jumps from dynamic_classify chain
- post_include "add rule inet dscpclassify dynamic_classify ip saddr . th sport . meta l4proto @threaded_clients goto threaded_client"
- post_include "add rule inet dscpclassify dynamic_classify ip6 saddr . th sport . meta l4proto @threaded_clients6 goto threaded_client"
+ post_include "add rule inet $TABLE dynamic_classify ip saddr . th sport . meta l4proto @bulk_clients goto bulk_client"
+ post_include "add rule inet $TABLE dynamic_classify ip6 saddr . th sport . meta l4proto @bulk_clients6 goto bulk_client"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip daddr . th dport . meta l4proto @threaded_clients goto threaded_client_reply"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip6 daddr . th dport . meta l4proto @threaded_clients6 goto threaded_client_reply"
+ post_include "add rule inet $TABLE dynamic_classify_reply ip daddr . th dport . meta l4proto @bulk_clients goto bulk_client_reply"
+ post_include "add rule inet $TABLE dynamic_classify_reply ip6 daddr . th dport . meta l4proto @bulk_clients6 goto bulk_client_reply"
}
-destroy_threaded_service_rules() {
- post_include "destroy chain inet dscpclassify threaded_service"
- post_include "destroy chain inet dscpclassify threaded_service_reply"
+destroy_high_throughput_service_rules() {
+ post_include "destroy chain inet $TABLE high_throughput_service"
+ post_include "destroy chain inet $TABLE high_throughput_service_reply"
- check_set_exists threaded_services && post_include "destroy set inet dscpclassify threaded_services"
- check_set_exists threaded_services6 && post_include "destroy set inet dscpclassify threaded_services6"
+ post_include "destroy set inet $TABLE high_throughput_services"
+ post_include "destroy set inet $TABLE high_throughput_services6"
}
-create_threaded_service_rules() {
- local class_high_throughput dynamic_classify threaded_service_detection threaded_service_min_bytes threaded_service_min_connections
+create_high_throughput_service_rules() {
+ local class="$class_high_throughput"
+ local enabled=1
+ local min_bytes=1000000
+ local min_connections=3
- config_get_bool dynamic_classify global dynamic_classify $DYNAMIC_CLASSIFY
- config_get_bool threaded_service_detection global threaded_service_detection $THREADED_SERVICE_DETECTION
- if [ "$dynamic_classify" != 1 ] || [ "$threaded_service_detection" != 1 ]; then
- destroy_threaded_service_rules
+ config_get_bool enabled "$high_throughput_service_detection_config" enabled "$enabled"
+ [ "$enabled" = 1 ] || {
+ destroy_high_throughput_service_rules
return 0
- fi
+ }
- config_get threaded_service_min_connections global threaded_service_min_connections $THREADED_SERVICE_MIN_CONNECTIONS
- if ! check_uint "$threaded_service_min_connections" || [ "$threaded_service_min_connections" -lt 2 ]; then
- log err "Global option threaded_service_min_connections contains an invalid value"
+ config_get min_bytes "$high_throughput_service_detection_config" min_bytes "$min_bytes"
+ if ! check_uint "$min_bytes" || [ "$min_bytes" = 0 ]; then
+ log err "high_throughput_service_detection config option 'min_bytes' contains an invalid value"
return 1
fi
- config_get threaded_service_min_bytes global threaded_service_min_bytes $THREADED_SERVICE_MIN_BYTES
- check_uint "$threaded_service_min_bytes" || {
- log err "Global option threaded_service_min_bytes contains an invalid value"
+ config_get min_connections "$high_throughput_service_detection_config" min_connections "$min_connections"
+ if ! check_uint "$min_connections" || [ "$min_connections" -lt 2 ]; then
+ log err "high_throughput_service_detection config option 'min_connections' contains an invalid value"
return 1
- }
- config_get class_high_throughput global class_high_throughput $CLASS_HIGH_THROUGHPUT
- class_high_throughput="$(dscp_class "$class_high_throughput")" || {
- log err "Global option class_high_throughput contains an invalid DSCP class"
+ fi
+ config_get class "$high_throughput_service_detection_config" class "$class"
+ class="$(dscp_class "$class")" || {
+ log err "high_throughput_service_detection config option 'class' contains an invalid DSCP class"
return 1
}
- case "$class_bulk" in
- le) class_bulk=lephb ;; # RFC-8622
- cs0) destroy_threaded_service_rules; return 0 ;; # Skip rule creation as CS0 is the default
+ case "$class" in
+ le) class=lephb ;; # RFC-8622
+ cs0)
+ destroy_high_throughput_service_rules
+ log warning "Disabling threaded service detection as its configured class CS0/DF/BE is the default packet class"
+ return 0
+ ;;
esac
# Create sets for matching threaded services
- check_set_exists threaded_services || post_include "add set inet dscpclassify threaded_services { type ipv4_addr . ipv4_addr . inet_service . inet_proto; flags timeout; }"
- check_set_exists threaded_services6 || post_include "add set inet dscpclassify threaded_services6 { type ipv6_addr . ipv6_addr . inet_service . inet_proto; flags timeout; }"
+ check_set_exists high_throughput_services || post_include "add set inet $TABLE high_throughput_services { type ipv4_addr . ipv4_addr . inet_service . inet_proto; flags timeout; }"
+ check_set_exists high_throughput_services6 || post_include "add set inet $TABLE high_throughput_services6 { type ipv6_addr . ipv6_addr . inet_service . inet_proto; flags timeout; }"
# Create threaded service detection rules
- post_include "add rule inet dscpclassify established_connection meter ts_detect { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 5s limit rate over $((threaded_service_min_connections - 1))/minute } add @threaded_services { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 30s }"
- post_include "add rule inet dscpclassify established_connection meter ts_detect6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 5s limit rate over $((threaded_service_min_connections - 1))/minute } add @threaded_services6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 30s }"
+ post_include "add rule inet $TABLE established_connection meter high_throughput_service_detect { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 5s limit rate over $((min_connections - 1))/minute } add @high_throughput_services { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 30s }"
+ post_include "add rule inet $TABLE established_connection meter high_throughput_service_detect6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 5s limit rate over $((min_connections - 1))/minute } add @high_throughput_services6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 30s }"
# Create threaded service classification rule chains
- post_include "add chain inet dscpclassify threaded_service"
- post_include "add rule inet dscpclassify threaded_service ct original bytes < $threaded_service_min_bytes return"
- post_include "add rule inet dscpclassify threaded_service update @threaded_services { ip saddr . ip daddr and 255.255.255.0 . th dport . meta l4proto timeout 5m }"
- post_include "add rule inet dscpclassify threaded_service update @threaded_services6 { ip6 saddr . ip6 daddr and ffff:ffff:ffff:: . th dport . meta l4proto timeout 5m }"
- post_include "add rule inet dscpclassify threaded_service ct mark set ct mark | \$${class_high_throughput} return"
-
- post_include "add chain inet dscpclassify threaded_service_reply"
- post_include "add rule inet dscpclassify threaded_service_reply ct reply bytes < $threaded_service_min_bytes return"
- post_include "add rule inet dscpclassify threaded_service_reply update @threaded_services { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 5m }"
- post_include "add rule inet dscpclassify threaded_service_reply update @threaded_services6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 5m }"
- post_include "add rule inet dscpclassify threaded_service_reply ct mark set ct mark | \$${class_high_throughput} return"
+ post_include "add chain inet $TABLE high_throughput_service"
+ post_include "add rule inet $TABLE high_throughput_service ct original bytes < $min_bytes return"
+ post_include "add rule inet $TABLE high_throughput_service update @high_throughput_services { ip saddr . ip daddr and 255.255.255.0 . th dport . meta l4proto timeout 5m }"
+ post_include "add rule inet $TABLE high_throughput_service update @high_throughput_services6 { ip6 saddr . ip6 daddr and ffff:ffff:ffff:: . th dport . meta l4proto timeout 5m }"
+ post_include "add rule inet $TABLE high_throughput_service ct mark set ct mark | \$${class} return"
+
+ post_include "add chain inet $TABLE high_throughput_service_reply"
+ post_include "add rule inet $TABLE high_throughput_service_reply ct reply bytes < $min_bytes return"
+ post_include "add rule inet $TABLE high_throughput_service_reply update @high_throughput_services { ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto timeout 5m }"
+ post_include "add rule inet $TABLE high_throughput_service_reply update @high_throughput_services6 { ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto timeout 5m }"
+ post_include "add rule inet $TABLE high_throughput_service_reply ct mark set ct mark | \$${class} return"
# Create jumps from dynamic_classify chain
- post_include "add rule inet dscpclassify dynamic_classify ip saddr . ip daddr and 255.255.255.0 . th dport . meta l4proto @threaded_services goto threaded_service"
- post_include "add rule inet dscpclassify dynamic_classify ip6 saddr . ip6 daddr and ffff:ffff:ffff:: . th dport . meta l4proto @threaded_services6 goto threaded_service"
+ post_include "add rule inet $TABLE dynamic_classify ip saddr . ip daddr and 255.255.255.0 . th dport . meta l4proto @high_throughput_services goto high_throughput_service"
+ post_include "add rule inet $TABLE dynamic_classify ip6 saddr . ip6 daddr and ffff:ffff:ffff:: . th dport . meta l4proto @high_throughput_services6 goto high_throughput_service"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto @threaded_services goto threaded_service_reply"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto @threaded_services6 goto threaded_service_reply"
+ post_include "add rule inet $TABLE dynamic_classify_reply ip daddr . ip saddr and 255.255.255.0 . th sport . meta l4proto @high_throughput_services goto high_throughput_service_reply"
+ post_include "add rule inet $TABLE dynamic_classify_reply ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto @high_throughput_services6 goto high_throughput_service_reply"
}
create_dscp_mark_rule() {
- local wmm
+ local wmm_mark_lan=1
- config_get_bool wmm global wmm $WMM
- [ "$wmm" = 1 ] && post_include "add rule inet dscpclassify postrouting oifname \$lan ct mark & \$ct_dscp vmap @ct_wmm"
+ config_get_bool wmm_mark_lan "$service_config" wmm_mark_lan "$wmm_mark_lan"
+ [ "$wmm_mark_lan" = 1 ] && post_include "add rule inet $TABLE postrouting oifname \$lan ct mark & \$ct_dscp vmap @ct_wmm"
- post_include "add rule inet dscpclassify postrouting ct mark & \$ct_dscp vmap @ct_dscp"
+ post_include "add rule inet $TABLE postrouting ct mark & \$ct_dscp vmap @ct_dscp"
}
create_flush_actions() {
- for chain in $(nft -j list chains | jsonfilter -e '@.nftables[@.chain.table="dscpclassify"].chain.name'); do
- pre_include "flush chain inet dscpclassify ${chain}"
+ for chain in $(nft -j list chains | jsonfilter -e "@.nftables[@.chain.table=\"${TABLE}\"].chain.name"); do
+ pre_include "flush chain inet $TABLE ${chain}"
done
- for map in $(nft -j list maps | jsonfilter -e '@.nftables[@.map.table="dscpclassify"].map.name'); do
- pre_include "flush map inet dscpclassify ${map}"
+ for map in $(nft -j list maps | jsonfilter -e "@.nftables[@.map.table=\"${TABLE}\"].map.name"); do
+ pre_include "flush map inet $TABLE ${map}"
done
- # Destroy orphaned meter sets
- check_set_exists tc_detect && pre_include "destroy set inet dscpclassify tc_detect"
- check_set_exists tc_detect6 && pre_include "destroy set inet dscpclassify tc_detect6"
- check_set_exists tc_orig_bulk && pre_include "destroy set inet dscpclassify tc_orig_bulk"
- check_set_exists tc_orig_bulk6 && pre_include "destroy set inet dscpclassify tc_orig_bulk6"
- check_set_exists tc_reply_bulk && pre_include "destroy set inet dscpclassify tc_reply_bulk"
- check_set_exists tc_reply_bulk6 && pre_include "destroy set inet dscpclassify tc_reply_bulk6"
- check_set_exists ts_detect && pre_include "destroy set inet dscpclassify ts_detect"
- check_set_exists ts_detect6 && pre_include "destroy set inet dscpclassify ts_detect6"
+ pre_include "destroy set inet $TABLE bulk_client_detect"
+ pre_include "destroy set inet $TABLE bulk_client_detect6"
+ pre_include "destroy set inet $TABLE bulk_client_orig_classify"
+ pre_include "destroy set inet $TABLE bulk_client_orig_classify6"
+ pre_include "destroy set inet $TABLE bulk_client_reply_classify"
+ pre_include "destroy set inet $TABLE bulk_client_reply_classify6"
+ pre_include "destroy set inet $TABLE high_throughput_service_detect"
+ pre_include "destroy set inet $TABLE high_throughput_service_detect6"
}
create_pre_include() {
rm -f "$PRE_INCLUDE"
- pre_include "define lan = { $(format_list "$lan" ", " "\"") }"
- pre_include "define wan = { $(format_list "$wan" ", " "\"") }"
+ pre_include "define lan = $(nft_interface_list "$lan")"
+ pre_include "define wan = $(nft_interface_list "$wan")"
- pre_include "add table inet dscpclassify"
+ pre_include "add table inet $TABLE"
[ "$action" = "reload" ] && create_flush_actions
pre_include "include \"${VERDICTS}\""
@@ -808,48 +953,145 @@ create_pre_include() {
create_post_include() {
rm -f "$POST_INCLUDE"
- config_foreach create_user_set set # depreciating in favour of 'ipset' section name for consistency with fw4
+ config_foreach create_user_set set # deprecating in favour of 'ipset' section name for consistency with fw4
config_foreach create_user_set ipset # section name consistent with fw4
config_foreach_reverse create_user_rule rule
- create_client_classify_jump || return 1
- create_dynamic_classify_jump || return 1
- create_threaded_client_rules || return 1
- create_threaded_service_rules || return 1
+ create_client_class_adoption_rules || return 1
+ create_dynamic_classify_rules || return 1
+ create_bulk_client_rules || return 1
+ create_high_throughput_service_rules || return 1
create_dscp_mark_rule || return 1
}
get_zones() {
- local dev lan_zones wan_zones
+ local interfaces lan_zones wan_zones
- config_get lan global lan_device
- config_get lan_zones global lan_zone "lan"
-
- for i in $lan_zones; do
- dev="$(fw_zone_devices "$i")" && lan="${lan:+$lan }$dev"
+ config_get lan "$service_config" lan_device # L3 physical interface
+ config_get lan_zones "$service_config" lan_zone "lan"
+ for zone in $lan_zones; do
+ interfaces="$(fw_zone_interfaces "$zone")" && lan="${lan:+$lan }${interfaces}"
done
[ -n "$lan" ] || return 1
- config_get wan global wan_device
- config_get wan_zones global wan_zone "wan"
-
- for i in $wan_zones; do
- dev="$(fw_zone_devices "$i")" && wan="${wan:+$wan }$dev"
+ config_get wan "$service_config" wan_device # L3 physical interface
+ config_get wan_zones "$service_config" wan_zone "wan"
+ for zone in $wan_zones; do
+ interfaces="$(fw_zone_interfaces "$zone")" && wan="${wan:+$wan }${interfaces}"
done
[ -n "$wan" ] || return 1
}
+migrate_config() {
+ local changed=0
+
+ # Ensure a section exists, create it if missing
+ ensure_section_exists() {
+ local type="$1"
+ uci get "$CONFIG.@${type}[0]" &>/dev/null || {
+ uci add $CONFIG "$type" >/dev/null
+ changed=1
+ }
+ }
+
+ # Move an option from one section to another
+ move_unique_section_option() {
+ local from_type="$1" from_opt="$2" to_type="$3" to_opt="$4"
+ value=$(uci get "$CONFIG.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
+ ensure_section_exists "$to_type"
+ uci set "$CONFIG.@${to_type}[0].${to_opt}=${value}"
+ uci delete "$CONFIG.@${from_type}[0].${from_opt}"
+ changed=1
+ return 0
+ }
+
+ # Rename a section type
+ rename_section_type() {
+ local from_type="$1" to_type="$2"
+ for idx in $(uci show "$CONFIG" | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
+ uci rename "$CONFIG.@${from_type}[$idx]=${to_type}"
+ changed=1
+ done
+ }
+
+ # Migrate global options to new section types
+ if uci get "$CONFIG.@global[0]" &>/dev/null; then
+ # Options for the 'service' section
+ move_unique_section_option global lan_device service lan_device
+ move_unique_section_option global lan_zone service lan_zone
+ move_unique_section_option global wan_device service wan_device
+ move_unique_section_option global wan_zone service wan_zone
+ move_unique_section_option global debug service debug
+ move_unique_section_option global class_bulk service class_low_effort
+ move_unique_section_option global class_low_effort service class_low_effort
+ move_unique_section_option global class_high_throughput service class_high_throughput
+ move_unique_section_option global wmm service wmm_mark_lan
+
+ # Options for the 'client_class_adoption' section
+ move_unique_section_option global client_hints client_class_adoption enabled
+
+ # Options for the 'bulk_client_detection' section
+ move_unique_section_option global threaded_client_detection bulk_client_detection enabled
+ move_unique_section_option global threaded_client_min_bytes bulk_client_detection min_bytes
+ move_unique_section_option global threaded_client_min_connections bulk_client_detection min_connections
+
+ # Options for the 'high_throughput_service_detection' section
+ move_unique_section_option global threaded_service_detection high_throughput_service_detection enabled
+ move_unique_section_option global threaded_service_min_bytes high_throughput_service_detection min_bytes
+ move_unique_section_option global threaded_service_min_connections high_throughput_service_detection min_connections
+
+ uci delete "$CONFIG.@global[0]"
+ changed=1
+ fi
+
+ # Rename all 'set' type sections to 'ipset'
+ rename_section_type set ipset
+
+ [ "$changed" = 1 ] && {
+ uci commit "$CONFIG"
+ log info "Service config migrated"
+ }
+ return 0
+}
+
setup() {
local lan wan
+ local service_config
+ local client_class_adoption_config
+ local bulk_client_detection_config
+ local high_throughput_service_detection_config
+
+ local class_low_effort="le"
+ local class_high_throughput="af13"
+ local destroy_action
+
+ check_minimum_kernel_release 5.13 || {
+ log info "Falling back to CS1 for default Low Effort class due to Kernel version < 5.13"
+ class_low_effort="cs1"
+ }
+ if check_minimum_kernel_release 6.3; then
+ destroy_action="destroy"
+ else
+ log info "Falling back to nft delete due to Kernel version < 6.3"
+ destroy_action="delete"
+ fi
cleanup_setup
-
- config_load dscpclassify || {
+ migrate_config
+ config_load "$CONFIG" || {
log err "Failed to load config file"
return 1
}
- config_get_bool debug global debug "$debug"
+
+ config_get_exclusive_section service_config service
+ config_get_exclusive_section client_class_adoption_config client_class_adoption
+ config_get_exclusive_section bulk_client_detection_config bulk_client_detection
+ config_get_exclusive_section high_throughput_service_detection_config high_throughput_service_detection
+
+ config_get_bool debug "$service_config" debug "$DEBUG"
+ config_get class_low_effort "$service_config" class_low_effort "$class_low_effort"
+ config_get class_high_throughput "$service_config" class_high_throughput "$class_high_throughput"
get_zones || {
log info "Deferring ${action} because lan/wan firewall zones are unavailable"
@@ -880,7 +1122,7 @@ setup() {
setup_failed() {
log warning "Service ${action} failed"
- [ "$debug" = 1 ] && create_debug_file
+ [ "${debug:-$DEBUG}" = 1 ] && create_debug_file
destroy_table
delete_includes
}
@@ -897,7 +1139,7 @@ start_service() {
reload_service() {
/etc/init.d/dscpclassify status &>/dev/null || {
- echo "The dscpclassify service is not loaded"
+ echo "The $SERVICE_NAME service is not loaded"
return 1
}
start_service