From ce9dab03db5b40fc7e3d557541d06fa714f846e7 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Tue, 25 Mar 2025 17:53:03 +0000
Subject: [PATCH 01/17] Restructure config file and update readme to improve
readability
---
README.md | 164 ++++++----
etc/config/dscpclassify | 48 +--
etc/dscpclassify.d/main.nft | 4 +-
etc/init.d/dscpclassify | 627 +++++++++++++++++++++---------------
4 files changed, 507 insertions(+), 336 deletions(-)
diff --git a/README.md b/README.md
index 07b5a19..fc756d5 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/CS1**) by default and therefore prioritised **below Best Effort (CSO)** 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 (CS0/BE/DF)** traffic and **above Low Effort (LE)** traffic
+ * **diffserv3/4**: prioritised **equal to Best Effort (CS0/BE/DF)** 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,55 @@ 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_bulk | string | no | le | The default DSCP class applied to bulk 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) |
+
+### 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 +131,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 +173,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..15c467a 100644
--- a/etc/config/dscpclassify
+++ b/etc/config/dscpclassify
@@ -1,10 +1,22 @@
-config global 'global'
+config service
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'
+ 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 +42,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..2340918 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,40 +16,24 @@ 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=""
-
log() {
- logger -t dscpclassify -p "daemon.$1" "$2"
- case "$1" in
- info | warning | err) echo "$2" ;;
+ local level="$1" message="$2"
+ logger -t dscpclassify -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 "dscpclassify${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 "${nft_result:+$nft_result}"
echo $'\n'"<--- nft list table inet dscpclassify --->"
nft list table inet dscpclassify 2>&1
} > "$DEBUG_FILE"
@@ -86,32 +71,50 @@ 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)
-
+ 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 +137,39 @@ 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
+}
+
+nft_element_list() {
+ format_list "$1" ", " "" "{}"
+}
+
+nft_interface_list() {
+ format_list "$1" ", " "\"" "{}"
}
-fw_zone_devices() {
- local dev
+nft_flag_list() {
+ format_list "$1" ", "
+}
- dev="$(fw4 -q zone "$1" | sort -u)"
- [ -n "$dev" ] || return 1
+nft_type_list() {
+ format_list "$1" " . "
+}
- echo "$dev"
+fw_zone_interfaces() {
+ local interfaces
+
+ interfaces="$(fw4 -q zone "$1" | sort -u)"
+ [ -n "$interfaces" ] || return 1
+ echo "$interfaces"
}
check_duration() {
@@ -174,7 +198,7 @@ check_set_name() {
log warning "Set is missing the name option"
return 1
;;
- threaded_clients | threaded_clients6 | threaded_services | threaded_services6)
+ bulk_clients | bulk_clients6 | high_throughput_services | high_throughput_services6)
log warning "Sets cannot overwrite built-in dscpclassify sets"
return 1
;;
@@ -312,7 +336,7 @@ parse_set_flags() {
flags="$flags timeout"
}
- [ -n "$flags" ] && flags="$(format_list "$flags" ", ")"
+ [ -n "$flags" ] && flags=$(nft_flag_list "$flags")
return 0
}
@@ -350,87 +374,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\";} }"
}
- [ -n "$entry$element" ] && post_include "add element inet dscpclassify $name { $(format_list "$entry $element" ", ") }"
+ [ -n "$entry$element" ] && post_include "add element inet dscpclassify $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 +467,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 +479,31 @@ rule_port() {
;;
esac
- [ -n "$2" ] || 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'"
+
+ [ -n "$ports" ] || return 0
+ 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 +512,40 @@ 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 +555,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 +566,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,47 +588,50 @@ 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\"}"
@@ -612,163 +646,222 @@ create_user_rule() {
return 0
}
-create_client_classify_jump() {
- local client_hints
+destroy_client_class_adoption_rules() {
+ post_include "destroy chain inet dscpclassify client_classify"
+}
- config_get_bool client_hints global client_hints $CLIENT_HINTS
- [ "$client_hints" = 1 ] || return 0
+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
+ }
+
+ for class in $(config_get "$client_class_adoption_config" exclude_class "$exclude_class_defaults"); 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
+ }
+
+ # Create client class adoption rules
+ post_include "add rule inet dscpclassify client_classify $saddr ip dscp != $exclude_class ip dscp vmap @dscp_ct"
+ post_include "add rule inet dscpclassify client_classify $saddr6 ip6 dscp != $exclude_class ip6 dscp vmap @dscp_ct"
+
+ # Create jump from input and postrouting chains
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_dynamic_classify_jump() {
- local dynamic_classify
+destroy_dynamic_classify_rules() {
+ post_include "destroy chain inet dscpclassify dynamic_classify"
+ post_include "destroy chain inet dscpclassify dynamic_classify_reply"
+ post_include "destroy chain inet dscpclassify 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"
}
-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 dscpclassify bulk_client"
+ post_include "destroy chain inet dscpclassify 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"
+ check_set_exists bulk_clients && post_include "destroy set inet dscpclassify bulk_clients"
+ check_set_exists bulk_clients6 && post_include "destroy set inet dscpclassify 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_bulk"
+ 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 dscpclassify bulk_clients { type ipv4_addr . inet_service . inet_proto; flags timeout; }"
+ check_set_exists bulk_clients6 || post_include "add set inet dscpclassify 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 dscpclassify established_connection meter tc_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 dscpclassify established_connection meter tc_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 dscpclassify bulk_client"
+ post_include "add rule inet dscpclassify bulk_client meter tc_orig_bulk { 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 dscpclassify bulk_client meter tc_orig_bulk6 { 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 dscpclassify bulk_client_reply"
+ post_include "add rule inet dscpclassify bulk_client_reply meter tc_reply_bulk { 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 dscpclassify bulk_client_reply meter tc_reply_bulk6 { 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 dscpclassify dynamic_classify ip saddr . th sport . meta l4proto @bulk_clients goto bulk_client"
+ post_include "add rule inet dscpclassify 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 dscpclassify dynamic_classify_reply ip daddr . th dport . meta l4proto @bulk_clients goto bulk_client_reply"
+ post_include "add rule inet dscpclassify 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 dscpclassify high_throughput_service"
+ post_include "destroy chain inet dscpclassify 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"
+ check_set_exists high_throughput_services && post_include "destroy set inet dscpclassify high_throughput_services"
+ check_set_exists high_throughput_services6 && post_include "destroy set inet dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify established_connection meter ts_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 dscpclassify established_connection meter ts_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 dscpclassify high_throughput_service"
+ post_include "add rule inet dscpclassify high_throughput_service ct original bytes < $min_bytes return"
+ post_include "add rule inet dscpclassify 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 dscpclassify 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 dscpclassify high_throughput_service ct mark set ct mark | \$${class} return"
+
+ post_include "add chain inet dscpclassify high_throughput_service_reply"
+ post_include "add rule inet dscpclassify high_throughput_service_reply ct reply bytes < $min_bytes return"
+ post_include "add rule inet dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify 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 dscpclassify postrouting oifname \$lan ct mark & \$ct_dscp vmap @ct_wmm"
post_include "add rule inet dscpclassify postrouting ct mark & \$ct_dscp vmap @ct_dscp"
}
@@ -795,8 +888,8 @@ create_flush_actions() {
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"
[ "$action" = "reload" ] && create_flush_actions
@@ -808,48 +901,72 @@ 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() {
+ return 0
+ # ipset section replaces set
+ # service section replaces global
+ # wmm_mark_lan option replaces wmm
+ # client_hints moves to client_class_adoption: enabled
+ # threaded_client_min_bytes moves to bulk_client_detection: min_bytes
+ # threaded_client_min_connections moves to bulk_client_detection: min_connections
+ # threaded_service_min_bytes moves to high_throughput_service_detection: min_bytes
+ # threaded_service_min_connections moves to high_throughput_service_detection: min_connections
+}
+
setup() {
local lan wan
+ local service_config
+ local client_class_adoption_config
+ local bulk_client_detection_config
+ local high_throughput_service_detection_config
- cleanup_setup
+ local class_bulk="le"
+ local class_high_throughput="af13"
+ cleanup_setup
config_load dscpclassify || {
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_bulk "$service_config" class_bulk "$class_bulk"
+ 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 +997,7 @@ setup() {
setup_failed() {
log warning "Service ${action} failed"
- [ "$debug" = 1 ] && create_debug_file
+ [ "${debug:-$DEBUG}" = 1 ] && create_debug_file
destroy_table
delete_includes
}
From 4d780d037f4466153fef9b5557aaad4a087fd0f7 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Mon, 31 Mar 2025 18:34:02 +0100
Subject: [PATCH 02/17] Fall back to class cs1 for low effort on kernel
releases < 5.13
Kernels prior to 5.13 do not have mappings for LE in the layer-cake diffserv tins
---
README.md | 12 +++++++-----
etc/config/dscpclassify | 2 --
etc/init.d/dscpclassify | 24 +++++++++++++++++++++---
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index fc756d5..450713b 100644
--- a/README.md
+++ b/README.md
@@ -22,12 +22,12 @@ 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.
### 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/CS1**) by default and therefore prioritised **below Best Effort (CSO)** traffic when using the layer-cake qdisc.
+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.
### 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 (CS0/BE/DF)** traffic and **above Low Effort (LE)** traffic
- * **diffserv3/4**: prioritised **equal to Best Effort (CS0/BE/DF)** traffic
+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
## Service architecture 🏗️
@@ -76,7 +76,7 @@ The service configuration is located in '/etc/config/dscpclassify'.
### Section "service"
|Name | Type | Required | Default | Description|
|--- | --- | --- | --- | ---|
-|class_bulk | string | no | le | The default DSCP class applied to bulk connections |
+|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**_ |
@@ -85,6 +85,8 @@ The service configuration is located in '/etc/config/dscpclassify'.
|_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|
|--- | --- | --- | --- | ---|
diff --git a/etc/config/dscpclassify b/etc/config/dscpclassify
index 15c467a..745a899 100644
--- a/etc/config/dscpclassify
+++ b/etc/config/dscpclassify
@@ -1,6 +1,4 @@
config service
- option class_bulk 'le'
- option class_high_throughput 'af13'
option wmm_mark_lan '0'
config client_class_adoption
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 2340918..68e1196 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -172,6 +172,22 @@ fw_zone_interfaces() {
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() {
echo "$1" | grep -q -E -e "^([1-9][0-9]*[smhd]){1,4}$"
}
@@ -722,7 +738,7 @@ destroy_bulk_client_rules() {
}
create_bulk_client_rules() {
- local class="$class_bulk"
+ local class="$class_low_effort"
local enabled=1
local min_bytes=10000
local min_connections=10
@@ -950,9 +966,11 @@ setup() {
local bulk_client_detection_config
local high_throughput_service_detection_config
- local class_bulk="le"
+ local class_low_effort="le"
local class_high_throughput="af13"
+ check_minimum_kernel_release 5.13 || class_low_effort="cs1"
+
cleanup_setup
config_load dscpclassify || {
log err "Failed to load config file"
@@ -965,7 +983,7 @@ setup() {
config_get_exclusive_section high_throughput_service_detection_config high_throughput_service_detection
config_get_bool debug "$service_config" debug "$DEBUG"
- config_get class_bulk "$service_config" class_bulk "$class_bulk"
+ 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 || {
From ab09a1b129abc42a6a88a2e3cc6ed60559cf5263 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Mon, 31 Mar 2025 20:11:30 +0100
Subject: [PATCH 03/17] Make check_set_against_existing function checks more
robust
---
etc/init.d/dscpclassify | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 68e1196..c55e2ed 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -229,10 +229,10 @@ 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
}
@@ -246,12 +246,12 @@ check_set_against_existing() {
existing_set=$(nft -t -j list set inet dscpclassify "$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
@@ -259,8 +259,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
From b174838770c521b38bb58ead0bb7ecf6cd524a07 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Mon, 31 Mar 2025 20:42:52 +0100
Subject: [PATCH 04/17] Fix exclude_class config option
---
etc/init.d/dscpclassify | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index c55e2ed..d74fc93 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -678,7 +678,8 @@ create_client_class_adoption_rules() {
return 0
}
- for class in $(config_get "$client_class_adoption_config" exclude_class "$exclude_class_defaults"); do
+ 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
From c021b323a2789fc158337a872b636c82ee3c25dc Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Tue, 1 Apr 2025 13:33:18 +0100
Subject: [PATCH 05/17] Update meter set names
---
etc/init.d/dscpclassify | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index d74fc93..022f594 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -218,7 +218,7 @@ check_set_name() {
log warning "Sets cannot overwrite built-in dscpclassify sets"
return 1
;;
- tc_detect | tc_detect6 | tc_orig_bulk | tc_orig_bulk6 | tc_reply_bulk | tc_reply_bulk6 | ts_detect | ts_detect6)
+ 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 dscpclassify meter sets"
return 1
;;
@@ -780,17 +780,17 @@ create_bulk_client_rules() {
check_set_exists bulk_clients6 || post_include "add set inet dscpclassify 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 $((min_connections - 1))/minute } add @bulk_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 $((min_connections - 1))/minute } add @bulk_clients6 { ip6 daddr . th dport . meta l4proto timeout 30s }"
+ post_include "add rule inet dscpclassify 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 dscpclassify 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 bulk_client"
- post_include "add rule inet dscpclassify bulk_client meter tc_orig_bulk { 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 dscpclassify bulk_client meter tc_orig_bulk6 { 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 rule inet dscpclassify 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 dscpclassify 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 bulk_client_reply"
- post_include "add rule inet dscpclassify bulk_client_reply meter tc_reply_bulk { 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 dscpclassify bulk_client_reply meter tc_reply_bulk6 { 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"
+ post_include "add rule inet dscpclassify 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 dscpclassify 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 @bulk_clients goto bulk_client"
@@ -850,8 +850,8 @@ create_high_throughput_service_rules() {
check_set_exists high_throughput_services6 || post_include "add set inet dscpclassify 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 $((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 dscpclassify established_connection meter ts_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 }"
+ post_include "add rule inet dscpclassify 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 dscpclassify 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 high_throughput_service"
@@ -892,14 +892,14 @@ create_flush_actions() {
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"
+ check_set_exists bulk_client_detect && pre_include "destroy set inet dscpclassify bulk_client_detect"
+ check_set_exists bulk_client_detect6 && pre_include "destroy set inet dscpclassify bulk_client_detect6"
+ check_set_exists bulk_client_orig_classify && pre_include "destroy set inet dscpclassify bulk_client_orig_classify"
+ check_set_exists bulk_client_orig_classify6 && pre_include "destroy set inet dscpclassify bulk_client_orig_classify6"
+ check_set_exists bulk_client_reply_classify && pre_include "destroy set inet dscpclassify bulk_client_reply_classify"
+ check_set_exists bulk_client_reply_classify6 && pre_include "destroy set inet dscpclassify bulk_client_reply_classify6"
+ check_set_exists high_throughput_service_detect && pre_include "destroy set inet dscpclassify high_throughput_service_detect"
+ check_set_exists high_throughput_service_detect6 && pre_include "destroy set inet dscpclassify high_throughput_service_detect6"
}
create_pre_include() {
From 3f0395c8064ab62d66e4997eb7f5323283884fd0 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Tue, 1 Apr 2025 13:42:09 +0100
Subject: [PATCH 06/17] Add log message for Low Effort default fallback
---
etc/init.d/dscpclassify | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 022f594..8fa7258 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -970,7 +970,10 @@ setup() {
local class_low_effort="le"
local class_high_throughput="af13"
- check_minimum_kernel_release 5.13 || class_low_effort="cs1"
+ check_minimum_kernel_release 5.13 || {
+ log warning "Falling back to CS1 for default Low Effort class due to Kernel version < 5.13"
+ class_low_effort="cs1"
+ }
cleanup_setup
config_load dscpclassify || {
From 8c66738a61c997628c7c4d5bebd33843198cb327 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Tue, 1 Apr 2025 13:43:07 +0100
Subject: [PATCH 07/17] Change Low Effort class fallback log level to info
---
etc/init.d/dscpclassify | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 8fa7258..098a593 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -971,7 +971,7 @@ setup() {
local class_high_throughput="af13"
check_minimum_kernel_release 5.13 || {
- log warning "Falling back to CS1 for default Low Effort class due to Kernel version < 5.13"
+ log info "Falling back to CS1 for default Low Effort class due to Kernel version < 5.13"
class_low_effort="cs1"
}
From 6792a30e4d74c0e9f05a0caf02b6460fc8aa7bbe Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Mon, 7 Apr 2025 13:36:19 +0100
Subject: [PATCH 08/17] Resolve issue with non tcp/udp protocol user rules
Fixes https://github.com/jeverley/dscpclassify/pull/36#issuecomment-2774302148
---
etc/init.d/dscpclassify | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 098a593..6442884 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -495,12 +495,13 @@ rule_port() {
;;
esac
+ [ -n "$ports" ] || return 0
+
check_port_proto "$protocol" || {
log warning "Rules cannot combine a ${direction}_port with protocols other than 'tcp' or 'udp'"
return 1
}
- [ -n "$ports" ] || return 0
parse_rule_ports "$ports" || {
log warning "Rule contains an invalid ${direction}_port"
return 1
@@ -534,6 +535,7 @@ rule_addr() {
log warning "Rule contains an invalid family"
return 1
fi
+
parse_rule_ips "$addresses" || {
log warning "Rule contains an invalid ${direction}_ip"
return 1
From 20310722797e7433bc34ae469fc5c3b5f9fb434c Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Thu, 19 Jun 2025 17:24:15 +0100
Subject: [PATCH 09/17] Implement function for migrating config file to new
structure
---
etc/init.d/dscpclassify | 75 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 66 insertions(+), 9 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 6442884..967a18f 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -951,15 +951,71 @@ get_zones() {
}
migrate_config() {
- return 0
- # ipset section replaces set
- # service section replaces global
- # wmm_mark_lan option replaces wmm
- # client_hints moves to client_class_adoption: enabled
- # threaded_client_min_bytes moves to bulk_client_detection: min_bytes
- # threaded_client_min_connections moves to bulk_client_detection: min_connections
- # threaded_service_min_bytes moves to high_throughput_service_detection: min_bytes
- # threaded_service_min_connections moves to high_throughput_service_detection: min_connections
+ local changed=0
+
+ # Ensure a section exists, create it if missing
+ ensure_section_exists() {
+ local type="$1"
+ uci get dscpclassify.@${type}[0] &>/dev/null || {
+ uci add dscpclassify "$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"
+ if uci get dscpclassify.@${from_type}[0].${from_opt} &>/dev/null; then
+ ensure_section_exists "$to_type"
+ uci set dscpclassify.@${to_type}[0].${to_opt}="$(uci get dscpclassify.@${from_type}[0].${from_opt})"
+ uci delete dscpclassify.@${from_type}[0].${from_opt}
+ changed=1
+ fi
+ }
+
+ # Rename a section type
+ rename_section_type() {
+ local from_type="$1" to_type="$2"
+ for idx in $(uci show dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
+ uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
+ changed=1
+ done
+ }
+
+ # Migrate global options to new section types
+ if uci get dscpclassify.@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_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 dscpclassify.@global[0]
+ changed=1
+ fi
+
+ # Rename all 'set' type sections to 'ipset'
+ rename_section_type set ipset
+
+ [ "$changed" = 1 ] && uci commit dscpclassify
+ return 0
}
setup() {
@@ -978,6 +1034,7 @@ setup() {
}
cleanup_setup
+ migrate_config
config_load dscpclassify || {
log err "Failed to load config file"
return 1
From 3e47cdf7202f56f831fb6f79fcc23b9c5411b675 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Mon, 23 Jun 2025 19:48:35 +0100
Subject: [PATCH 10/17] Config migration is logged
---
etc/init.d/dscpclassify | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 967a18f..4ab0766 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -1014,7 +1014,10 @@ migrate_config() {
# Rename all 'set' type sections to 'ipset'
rename_section_type set ipset
- [ "$changed" = 1 ] && uci commit dscpclassify
+ [ "$changed" = 1 ] && {
+ uci commit dscpclassify
+ log notice "Service config migrated"
+ }
return 0
}
From 68dc5ae2ab8fb19f50fcd808144002cd1c3d2799 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Mon, 23 Jun 2025 20:10:47 +0100
Subject: [PATCH 11/17] Indent consistency
---
etc/init.d/dscpclassify | 91 +++++++++++++++++++++--------------------
1 file changed, 46 insertions(+), 45 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 4ab0766..70d762c 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -951,74 +951,75 @@ get_zones() {
}
migrate_config() {
- local changed=0
-
- # Ensure a section exists, create it if missing
- ensure_section_exists() {
- local type="$1"
- uci get dscpclassify.@${type}[0] &>/dev/null || {
- uci add dscpclassify "$type" >/dev/null
- changed=1
+ local changed=0
+
+ # Ensure a section exists, create it if missing
+ ensure_section_exists() {
+ local type="$1"
+ uci get "dscpclassify.@${type}[0]" &>/dev/null || {
+ uci add dscpclassify "$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 "dscpclassify.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
+ ensure_section_exists "$to_type"
+ uci set "dscpclassify.@${to_type}[0].${to_opt}=${value}"
+ uci delete "dscpclassify.@${from_type}[0].${from_opt}"
+ changed=1
+ return 0
}
- }
-
- # 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"
- if uci get dscpclassify.@${from_type}[0].${from_opt} &>/dev/null; then
- ensure_section_exists "$to_type"
- uci set dscpclassify.@${to_type}[0].${to_opt}="$(uci get dscpclassify.@${from_type}[0].${from_opt})"
- uci delete dscpclassify.@${from_type}[0].${from_opt}
- changed=1
- fi
- }
# Rename a section type
rename_section_type() {
- local from_type="$1" to_type="$2"
- for idx in $(uci show dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
- uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
- changed=1
- done
- }
-
- # Migrate global options to new section types
- if uci get dscpclassify.@global[0] &>/dev/null; then
+ local from_type="$1" to_type="$2"
+ for idx in $(uci show dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
+ uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
+ changed=1
+ done
+ }
+
+ # Migrate global options to new section types
+ if uci get dscpclassify.@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
+ 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
+ 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
+ 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
+ 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 dscpclassify.@global[0]
- changed=1
- fi
+ uci delete dscpclassify.@global[0]
+ changed=1
+ fi
- # Rename all 'set' type sections to 'ipset'
- rename_section_type set ipset
+ # Rename all 'set' type sections to 'ipset'
+ rename_section_type set ipset
- [ "$changed" = 1 ] && {
- uci commit dscpclassify
- log notice "Service config migrated"
- }
- return 0
+ [ "$changed" = 1 ] && {
+ uci commit dscpclassify
+ log notice "Service config migrated"
+ }
+ return 0
}
setup() {
From a71ef4e0bc02fecbb3ddfcf8227a69c2c65fb0f4 Mon Sep 17 00:00:00 2001
From: Jack <46714706+jeverley@users.noreply.github.com>
Date: Mon, 23 Jun 2025 20:14:54 +0100
Subject: [PATCH 12/17] Config migration log level changed to info
---
etc/init.d/dscpclassify | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 70d762c..41f3db1 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -1017,7 +1017,7 @@ migrate_config() {
[ "$changed" = 1 ] && {
uci commit dscpclassify
- log notice "Service config migrated"
+ log info "Service config migrated"
}
return 0
}
From cf310b168cebf522c138a4937f7765979d1c0a30 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Sun, 31 Aug 2025 12:21:51 +0100
Subject: [PATCH 13/17] Indentation consistency
---
etc/init.d/dscpclassify | 74 ++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 37 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 41f3db1..83081ca 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -951,39 +951,39 @@ get_zones() {
}
migrate_config() {
- local changed=0
-
- # Ensure a section exists, create it if missing
- ensure_section_exists() {
- local type="$1"
- uci get "dscpclassify.@${type}[0]" &>/dev/null || {
- uci add dscpclassify "$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 "dscpclassify.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
- ensure_section_exists "$to_type"
- uci set "dscpclassify.@${to_type}[0].${to_opt}=${value}"
- uci delete "dscpclassify.@${from_type}[0].${from_opt}"
- changed=1
- return 0
- }
+ local changed=0
+
+ # Ensure a section exists, create it if missing
+ ensure_section_exists() {
+ local type="$1"
+ uci get "dscpclassify.@${type}[0]" &>/dev/null || {
+ uci add dscpclassify "$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 "dscpclassify.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
+ ensure_section_exists "$to_type"
+ uci set "dscpclassify.@${to_type}[0].${to_opt}=${value}"
+ uci delete "dscpclassify.@${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 dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
- uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
- changed=1
- done
- }
-
- # Migrate global options to new section types
- if uci get dscpclassify.@global[0] &>/dev/null; then
+ local from_type="$1" to_type="$2"
+ for idx in $(uci show dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
+ uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
+ changed=1
+ done
+ }
+
+ # Migrate global options to new section types
+ if uci get dscpclassify.@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
@@ -1012,14 +1012,14 @@ migrate_config() {
changed=1
fi
- # Rename all 'set' type sections to 'ipset'
- rename_section_type set ipset
+ # Rename all 'set' type sections to 'ipset'
+ rename_section_type set ipset
- [ "$changed" = 1 ] && {
- uci commit dscpclassify
- log info "Service config migrated"
- }
- return 0
+ [ "$changed" = 1 ] && {
+ uci commit dscpclassify
+ log info "Service config migrated"
+ }
+ return 0
}
setup() {
From f6e3fe023a82da92b379c71e665e02bde4030424 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Sun, 31 Aug 2025 13:35:11 +0100
Subject: [PATCH 14/17] Compatibility for kernels not supporting nft destroy
---
etc/init.d/dscpclassify | 52 +++++++++++++++++++++++++++++++++++------
1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index 83081ca..f432e30 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -16,6 +16,8 @@ MAIN="/etc/dscpclassify.d/main.nft"
VERDICTS="/etc/dscpclassify.d/verdicts.nft"
MAPS="/etc/dscpclassify.d/maps.nft"
+# Service nft table
+TABLE="dscpclassify"
log() {
local level="$1" message="$2"
@@ -48,7 +50,8 @@ delete_includes() {
}
destroy_table() {
- nft destroy table inet dscpclassify &>/dev/null
+ nft delete table inet "$TABLE" &>/dev/null
+ return 0
}
cleanup_service() {
@@ -62,14 +65,46 @@ 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
+}
+
+destroy_compatability() {
+ # Destroy is not supported in kernel versions < 6.3
+ # The latest kernels also return an error when destroy is used with a set that doesn't exist
+ 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=$(destroy_compatibility "$1") || return 0
+ echo "$command" >>"$PRE_INCLUDE"
}
post_include() {
- echo "$1" >>"$POST_INCLUDE"
+ local command
+ command=$(destroy_compatibility "$1") || return 0
+ echo "$command" >>"$POST_INCLUDE"
}
+
config_foreach_reverse() {
local ___function="$1"
local ___type="$2"
@@ -236,10 +271,6 @@ check_set_size() {
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
@@ -1031,11 +1062,18 @@ setup() {
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
migrate_config
From a3b6251d40efec10bcbc7a3c9188d4877b626a7c Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Sun, 31 Aug 2025 13:49:51 +0100
Subject: [PATCH 15/17] Use of service constants and remove redundant set check
---
etc/init.d/dscpclassify | 189 ++++++++++++++++++++--------------------
1 file changed, 95 insertions(+), 94 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index f432e30..ad6c0b7 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -16,12 +16,14 @@ MAIN="/etc/dscpclassify.d/main.nft"
VERDICTS="/etc/dscpclassify.d/verdicts.nft"
MAPS="/etc/dscpclassify.d/maps.nft"
-# Service nft table
-TABLE="dscpclassify"
+# Service constants
+SERVICE_NAME="dscpclassify"
+TABLE="$SERVICE_NAME"
+CONFIG="$SERVICE_NAME"
log() {
local level="$1" message="$2"
- logger -t dscpclassify -p "daemon.${level}" "$message"
+ logger -t "$SERVICE_NAME" -p "daemon.${level}" "$message"
case "$level" in
info | warning | err) >&2 echo "$message" ;;
esac
@@ -29,15 +31,15 @@ log() {
create_debug_file() {
{
- echo "dscpclassify${action:+ $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:+$nft_result}"
- echo $'\n'"<--- nft list table inet dscpclassify --->"
- nft list table inet dscpclassify 2>&1
+ echo $'\n'"<--- nft list table inet {$TABLE} --->"
+ nft list table inet "$TABLE" 2>&1
} > "$DEBUG_FILE"
}
@@ -250,11 +252,11 @@ check_set_name() {
return 1
;;
bulk_clients | bulk_clients6 | high_throughput_services | high_throughput_services6)
- log warning "Sets cannot overwrite built-in dscpclassify sets"
+ log warning "Sets cannot overwrite built-in $TABLE sets"
return 1
;;
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 dscpclassify meter sets"
+ log warning "Sets cannot overwrite built-in $TABLE meter sets"
return 1
;;
esac
@@ -275,7 +277,7 @@ 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/\s\+\.\s\+/\n/g')"
existing_type="$(jsonfilter -s "$existing_set" -e "@.nftables[*].set.type")"
@@ -463,11 +465,11 @@ create_user_set() {
[ "$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 $(nft_element_list "$entry $element")"
+ [ -n "$entry$element" ] && post_include "add element inet $TABLE $name $(nft_element_list "$entry $element")"
return 0
}
@@ -683,20 +685,20 @@ create_user_rule() {
[ "$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
}
destroy_client_class_adoption_rules() {
- post_include "destroy chain inet dscpclassify client_classify"
+ post_include "destroy chain inet $TABLE client_classify"
}
create_client_class_adoption_rules() {
@@ -734,18 +736,18 @@ create_client_class_adoption_rules() {
}
# Create client class adoption rules
- post_include "add rule inet dscpclassify client_classify $saddr ip dscp != $exclude_class ip dscp vmap @dscp_ct"
- post_include "add rule inet dscpclassify client_classify $saddr6 ip6 dscp != $exclude_class ip6 dscp vmap @dscp_ct"
+ 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"
# Create jump from input and postrouting chains
- 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"
+ 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"
}
destroy_dynamic_classify_rules() {
- post_include "destroy chain inet dscpclassify dynamic_classify"
- post_include "destroy chain inet dscpclassify dynamic_classify_reply"
- post_include "destroy chain inet dscpclassify established_connection"
+ 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() {
@@ -759,16 +761,16 @@ create_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_bulk_client_rules() {
- post_include "destroy chain inet dscpclassify bulk_client"
- post_include "destroy chain inet dscpclassify bulk_client_reply"
+ post_include "destroy chain inet $TABLE bulk_client"
+ post_include "destroy chain inet $TABLE bulk_client_reply"
- check_set_exists bulk_clients && post_include "destroy set inet dscpclassify bulk_clients"
- check_set_exists bulk_clients6 && post_include "destroy set inet dscpclassify bulk_clients6"
+ post_include "destroy set inet $TABLE bulk_clients"
+ post_include "destroy set inet $TABLE bulk_clients6"
}
create_bulk_client_rules() {
@@ -809,36 +811,36 @@ create_bulk_client_rules() {
esac
# Create sets for matching threaded clients
- check_set_exists bulk_clients || post_include "add set inet dscpclassify bulk_clients { type ipv4_addr . inet_service . inet_proto; flags timeout; }"
- check_set_exists bulk_clients6 || post_include "add set inet dscpclassify bulk_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 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 dscpclassify 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 }"
+ 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 bulk_client"
- post_include "add rule inet dscpclassify 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 dscpclassify 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 $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 bulk_client_reply"
- post_include "add rule inet dscpclassify 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 dscpclassify 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"
+ 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 @bulk_clients goto bulk_client"
- post_include "add rule inet dscpclassify dynamic_classify ip6 saddr . th sport . meta l4proto @bulk_clients6 goto bulk_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 @bulk_clients goto bulk_client_reply"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip6 daddr . th dport . meta l4proto @bulk_clients6 goto bulk_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_high_throughput_service_rules() {
- post_include "destroy chain inet dscpclassify high_throughput_service"
- post_include "destroy chain inet dscpclassify high_throughput_service_reply"
+ post_include "destroy chain inet $TABLE high_throughput_service"
+ post_include "destroy chain inet $TABLE high_throughput_service_reply"
- check_set_exists high_throughput_services && post_include "destroy set inet dscpclassify high_throughput_services"
- check_set_exists high_throughput_services6 && post_include "destroy set inet dscpclassify high_throughput_services6"
+ post_include "destroy set inet $TABLE high_throughput_services"
+ post_include "destroy set inet $TABLE high_throughput_services6"
}
create_high_throughput_service_rules() {
@@ -879,60 +881,59 @@ create_high_throughput_service_rules() {
esac
# Create sets for matching threaded services
- check_set_exists high_throughput_services || post_include "add set inet dscpclassify 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 dscpclassify high_throughput_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 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 dscpclassify 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 }"
+ 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 high_throughput_service"
- post_include "add rule inet dscpclassify high_throughput_service ct original bytes < $min_bytes return"
- post_include "add rule inet dscpclassify 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 dscpclassify 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 dscpclassify high_throughput_service ct mark set ct mark | \$${class} return"
-
- post_include "add chain inet dscpclassify high_throughput_service_reply"
- post_include "add rule inet dscpclassify high_throughput_service_reply ct reply bytes < $min_bytes return"
- post_include "add rule inet dscpclassify 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 dscpclassify 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 dscpclassify high_throughput_service_reply ct mark set ct mark | \$${class} 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 @high_throughput_services goto high_throughput_service"
- post_include "add rule inet dscpclassify 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 $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 @high_throughput_services goto high_throughput_service_reply"
- post_include "add rule inet dscpclassify dynamic_classify_reply ip6 daddr . ip6 saddr and ffff:ffff:ffff:: . th sport . meta l4proto @high_throughput_services6 goto high_throughput_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_mark_lan=1
config_get_bool wmm_mark_lan "$service_config" wmm_mark_lan "$wmm_mark_lan"
- [ "$wmm_mark_lan" = 1 ] && post_include "add rule inet dscpclassify postrouting oifname \$lan ct mark & \$ct_dscp vmap @ct_wmm"
+ [ "$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 bulk_client_detect && pre_include "destroy set inet dscpclassify bulk_client_detect"
- check_set_exists bulk_client_detect6 && pre_include "destroy set inet dscpclassify bulk_client_detect6"
- check_set_exists bulk_client_orig_classify && pre_include "destroy set inet dscpclassify bulk_client_orig_classify"
- check_set_exists bulk_client_orig_classify6 && pre_include "destroy set inet dscpclassify bulk_client_orig_classify6"
- check_set_exists bulk_client_reply_classify && pre_include "destroy set inet dscpclassify bulk_client_reply_classify"
- check_set_exists bulk_client_reply_classify6 && pre_include "destroy set inet dscpclassify bulk_client_reply_classify6"
- check_set_exists high_throughput_service_detect && pre_include "destroy set inet dscpclassify high_throughput_service_detect"
- check_set_exists high_throughput_service_detect6 && pre_include "destroy set inet dscpclassify high_throughput_service_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() {
@@ -941,7 +942,7 @@ create_pre_include() {
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}\""
@@ -987,8 +988,8 @@ migrate_config() {
# Ensure a section exists, create it if missing
ensure_section_exists() {
local type="$1"
- uci get "dscpclassify.@${type}[0]" &>/dev/null || {
- uci add dscpclassify "$type" >/dev/null
+ uci get "$CONFIG.@${type}[0]" &>/dev/null || {
+ uci add $CONFIG "$type" >/dev/null
changed=1
}
}
@@ -996,10 +997,10 @@ migrate_config() {
# 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 "dscpclassify.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
+ value=$(uci get "$CONFIG.@${from_type}[0].${from_opt}" 2>/dev/null) || return 1
ensure_section_exists "$to_type"
- uci set "dscpclassify.@${to_type}[0].${to_opt}=${value}"
- uci delete "dscpclassify.@${from_type}[0].${from_opt}"
+ uci set "$CONFIG.@${to_type}[0].${to_opt}=${value}"
+ uci delete "$CONFIG.@${from_type}[0].${from_opt}"
changed=1
return 0
}
@@ -1007,14 +1008,14 @@ migrate_config() {
# Rename a section type
rename_section_type() {
local from_type="$1" to_type="$2"
- for idx in $(uci show dscpclassify | grep "=${from_type}$" | cut -d'[' -f2 | cut -d']' -f1); do
- uci rename "dscpclassify.@${from_type}[$idx]=${to_type}"
+ 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 dscpclassify.@global[0] &>/dev/null; then
+ 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
@@ -1039,7 +1040,7 @@ migrate_config() {
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 dscpclassify.@global[0]
+ uci delete "$CONFIG.@global[0]"
changed=1
fi
@@ -1047,7 +1048,7 @@ migrate_config() {
rename_section_type set ipset
[ "$changed" = 1 ] && {
- uci commit dscpclassify
+ uci commit "$CONFIG"
log info "Service config migrated"
}
return 0
@@ -1077,7 +1078,7 @@ setup() {
cleanup_setup
migrate_config
- config_load dscpclassify || {
+ config_load "$CONFIG" || {
log err "Failed to load config file"
return 1
}
@@ -1137,7 +1138,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
From c4e7ef2d661cd15ec30f4dd087d225f7499cf0b3 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Sun, 31 Aug 2025 13:57:31 +0100
Subject: [PATCH 16/17] Rename compatibility function
---
etc/init.d/dscpclassify | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index ad6c0b7..efac41d 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -79,9 +79,10 @@ check_table_exists() {
nft -t list table inet "$TABLE" "$1" &>/dev/null
}
-destroy_compatability() {
+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 a set that doesn't exist
+ # 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}')
@@ -96,13 +97,13 @@ destroy_compatability() {
pre_include() {
local command
- command=$(destroy_compatibility "$1") || return 0
+ command=$(nft_compatibility "$1") || return 0
echo "$command" >>"$PRE_INCLUDE"
}
post_include() {
local command
- command=$(destroy_compatibility "$1") || return 0
+ command=$(nft_compatibility "$1") || return 0
echo "$command" >>"$POST_INCLUDE"
}
From dc90a4121d13714899c8842746d5200b6be34435 Mon Sep 17 00:00:00 2001
From: jeverley <46714706+jeverley@users.noreply.github.com>
Date: Sun, 31 Aug 2025 14:03:21 +0100
Subject: [PATCH 17/17] Fix spacing in rename_section_type
---
etc/init.d/dscpclassify | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/etc/init.d/dscpclassify b/etc/init.d/dscpclassify
index efac41d..3d3383d 100644
--- a/etc/init.d/dscpclassify
+++ b/etc/init.d/dscpclassify
@@ -1009,7 +1009,7 @@ migrate_config() {
# 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
+ 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