1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-03-09 15:40:03 +00:00

Update sqm autorate

This commit is contained in:
Ycarus (Yannick Chabanois) 2023-07-07 20:02:40 +02:00
parent 8b270e7264
commit ecf0b89798
6 changed files with 407 additions and 253 deletions

View file

@ -31,14 +31,14 @@ _config_autorate() {
[ "${min_upload}" == "0" ] || [ "${max_upload}" == "0" ] || [ "${upload}" == "0" ] && return [ "${min_upload}" == "0" ] || [ "${max_upload}" == "0" ] || [ "${upload}" == "0" ] && return
# config_get interface "$1" interface # config_get interface "$1" interface
# cp /usr/share/sqm-autorate/cake-autorate_template.sh /usr/share/sqm-autorate/cake-autorate_config.${interface}.sh # cp /usr/share/sqm-autorate/cake-autorate_template.sh /usr/share/sqm-autorate/cake-autorate_config.${interface}.sh
cp /usr/share/sqm-autorate/cake-autorate_template.sh /usr/share/sqm-autorate/cake-autorate_config.$1.sh cp /usr/share/sqm-autorate/config_template.sh /usr/share/sqm-autorate/config.$1.sh
} }
_launch_autorate() { _launch_autorate() {
logger -t "SQM-autorate" "Launch..." logger -t "SQM-autorate" "Launch..."
procd_open_instance procd_open_instance
# shellcheck disable=SC2086 # shellcheck disable=SC2086
procd_set_param command /usr/share/sqm-autorate/cake-autorate_launcher.sh procd_set_param command /usr/share/sqm-autorate/launcher.sh
procd_set_param limits nofile="51200 51200" procd_set_param limits nofile="51200 51200"
procd_set_param respawn 0 10 0 procd_set_param respawn 0 10 0
procd_set_param stderr 1 procd_set_param stderr 1
@ -46,16 +46,12 @@ _launch_autorate() {
} }
start_service() { start_service() {
rm -f /usr/share/sqm-autorate/config.*.sh
config_load sqm config_load sqm
config_foreach _config_autorate queue config_foreach _config_autorate queue
_launch_autorate _launch_autorate
} }
stop_service() {
rm -f /usr/share/sqm-autorate/cake-autorate_config.*.sh
pkill -9 cake-autorate
}
reload_service() { reload_service() {
stop stop
start start

View file

@ -1,22 +1,22 @@
#!/bin/bash #!/bin/bash
# CAKE-autorate automatically adjusts CAKE bandwidth(s) # cake-autorate automatically adjusts CAKE bandwidth(s)
# in dependence on: a) receive and transmit transfer rates; and b) latency # in dependence on: a) receive and transmit transfer rates; and b) latency
# (or can just be used to monitor and log transfer rates and latency) # (or can just be used to monitor and log transfer rates and latency)
# requires packages: bash; and one of the supported ping binaries # requires: bash; and one of the supported ping binaries
# each cake-autorate instance must be configured using a corresponding config file # each cake-autorate instance must be configured using a corresponding config file
# Project homepage: https://github.com/lynxthecat/cake-autorate # Project homepage: https://github.com/lynxthecat/cake-autorate
# Licence details: https://github.com/lynxthecat/cake-autorate/blob/master/LICENCE.md # Licence details: https://github.com/lynxthecat/cake-autorate/blob/master/LICENCE.md
# Author: @Lynx (OpenWrt forum) # Author and maintainer: lynxthecat
# Inspiration taken from: @moeller0 (OpenWrt forum) # Contributors: rany2; moeller0; richb-hanover
cake_autorate_version="2.0.0" cake_autorate_version="2.0.0"
## cake-autorate uses multiple asynchronous processes including ## cake-autorate uses multiple asynchronous processes including:
## main - main process ## main - main process
## monitor_achieved_rates - monitor network transfer rates ## monitor_achieved_rates - monitor network transfer rates
## maintain_pingers - manage pingers and active reflectors ## maintain_pingers - manage pingers and active reflectors
@ -26,7 +26,7 @@ cake_autorate_version="2.0.0"
## ##
## IPC is facilitated via FIFOs in the form of anonymous pipes ## IPC is facilitated via FIFOs in the form of anonymous pipes
## accessible via fds in the form: ${process_name_fd} ## accessible via fds in the form: ${process_name_fd}
## thereby to enable transferring commands and data between processes ## thereby to enable transferring instructions and data between processes
# Initialize file descriptors # Initialize file descriptors
## -1 signifies that the log file fd will not be used and ## -1 signifies that the log file fd will not be used and
@ -60,15 +60,20 @@ export LC_ALL=C
# Set PREFIX # Set PREFIX
PREFIX=/root/cake-autorate PREFIX=/root/cake-autorate
# shellcheck source=cake-autorate_lib.sh # shellcheck source=lib.sh
. "${PREFIX}/cake-autorate_lib.sh" . "${PREFIX}/lib.sh"
# shellcheck source=cake-autorate_defaults.sh # shellcheck source=defaults.sh
. "${PREFIX}/cake-autorate_defaults.sh" . "${PREFIX}/defaults.sh"
# get valid config overrides
mapfile -t valid_config_entries < <(grep -E '^[^(#| )].*=' "${PREFIX}/defaults.sh" | sed -e 's/[\t ]*\#.*//g' -e 's/=.*//g')
trap cleanup_and_killall INT TERM EXIT trap cleanup_and_killall INT TERM EXIT
cleanup_and_killall() cleanup_and_killall()
{ {
# Do not fail on error for this critical cleanup code
set +e
trap true INT TERM EXIT trap true INT TERM EXIT
log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}" log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
@ -88,13 +93,19 @@ cleanup_and_killall()
# terminate any processes that remain, save for main and intercept_stderr # terminate any processes that remain, save for main and intercept_stderr
unset "proc_pids[main]" unset "proc_pids[main]"
intercept_stderr_pid="${proc_pids[intercept_stderr]}" intercept_stderr_pid="${proc_pids[intercept_stderr]:-}"
if [[ -n "${intercept_stderr_pid}" ]]
then
unset "proc_pids[intercept_stderr]" unset "proc_pids[intercept_stderr]"
fi
terminate "${proc_pids[@]}" terminate "${proc_pids[@]}"
# restore original stderr, and terminate intercept_stderr # restore original stderr, and terminate intercept_stderr
if [[ -n "${intercept_stderr_pid}" ]]
then
exec 2>&"${original_stderr_fd}" exec 2>&"${original_stderr_fd}"
terminate "${intercept_stderr_pid}" terminate "${intercept_stderr_pid}"
fi
log_msg "SYSLOG" "Stopped cake-autorate with PID: ${BASHPID} and config: ${config_path}" log_msg "SYSLOG" "Stopped cake-autorate with PID: ${BASHPID} and config: ${config_path}"
@ -109,33 +120,31 @@ log_msg()
local type="${1}" local type="${1}"
local msg="${2}" local msg="${2}"
local instance_id="${instance_id:-"unknown"}" local instance_id="${instance_id:-"unknown"}"
local log_timestamp=${EPOCHREALTIME}
case ${type} in case ${type} in
DEBUG) DEBUG)
[[ "${debug}" == "0" ]] && return # skip over DEBUG messages where debug disabled ((debug == 0)) && return # skip over DEBUG messages where debug disabled
log_timestamp=${EPOCHREALTIME}
((log_DEBUG_messages_to_syslog)) && ((use_logger)) && logger -t "cake-autorate.${instance_id}" "${type}: ${log_timestamp} ${msg}" ((log_DEBUG_messages_to_syslog)) && ((use_logger)) && logger -t "cake-autorate.${instance_id}" "${type}: ${log_timestamp} ${msg}"
;; ;;
ERROR) ERROR)
log_timestamp=${EPOCHREALTIME}
((use_logger)) && logger -t "cake-autorate.${instance_id}" "${type}: ${log_timestamp} ${msg}" ((use_logger)) && logger -t "cake-autorate.${instance_id}" "${type}: ${log_timestamp} ${msg}"
;; ;;
SYSLOG) SYSLOG)
log_timestamp=${EPOCHREALTIME}
((use_logger)) && logger -t "cake-autorate.${instance_id}" "INFO: ${log_timestamp} ${msg}" ((use_logger)) && logger -t "cake-autorate.${instance_id}" "INFO: ${log_timestamp} ${msg}"
;; ;;
*) *)
log_timestamp=${EPOCHREALTIME}
;; ;;
esac esac
# Output to the log file fifo if available (for rotation handling) # Output to the log file fifo if available (for rotation handling)
# else output directly to the log file # else output directly to the log file
if (( log_fd >= 0 )); then if (( log_fd >= 0 ))
then
((log_to_file)) && printf '%s; %(%F-%H:%M:%S)T; %s; %s\n' "${type}" -1 "${log_timestamp}" "${msg}" >&"${log_fd}" ((log_to_file)) && printf '%s; %(%F-%H:%M:%S)T; %s; %s\n' "${type}" -1 "${log_timestamp}" "${msg}" >&"${log_fd}"
else else
((log_to_file)) && printf '%s; %(%F-%H:%M:%S)T; %s; %s\n' "${type}" -1 "${log_timestamp}" "${msg}" >> "${log_file_path}" ((log_to_file)) && printf '%s; %(%F-%H:%M:%S)T; %s; %s\n' "${type}" -1 "${log_timestamp}" "${msg}" >> "${log_file_path}"
@ -211,7 +220,8 @@ generate_log_file_scripts()
while [[ ! -f "${run_path}/last_log_file_export" ]] while [[ ! -f "${run_path}/last_log_file_export" ]]
do do
sleep 1 sleep 1
if (( ++read_try >= \${timeout_s} )); then if (( ++read_try >= \${timeout_s} ))
then
printf "ERROR: Timeout (\${timeout_s}s) reached before new log file export identified.\n" >&2 printf "ERROR: Timeout (\${timeout_s}s) reached before new log file export identified.\n" >&2
exit 1 exit 1
fi fi
@ -249,16 +259,19 @@ export_log_file()
log_msg "DEBUG" "Exporting log file with path: ${log_file_path/.log/_${log_file_export_datetime}.log}" log_msg "DEBUG" "Exporting log file with path: ${log_file_path/.log/_${log_file_export_datetime}.log}"
# Now export with or without compression to the appropriate export path # Now export with or without compression to the appropriate export path
if ((log_file_export_compress)); then if ((log_file_export_compress))
then
log_file_export_path="${log_file_export_path}.gz" log_file_export_path="${log_file_export_path}.gz"
if [[ -f "${log_file_path}.old" ]]; then if [[ -f "${log_file_path}.old" ]]
then
gzip -c "${log_file_path}.old" > "${log_file_export_path}" gzip -c "${log_file_path}.old" > "${log_file_export_path}"
gzip -c "${log_file_path}" >> "${log_file_export_path}" gzip -c "${log_file_path}" >> "${log_file_export_path}"
else else
gzip -c "${log_file_path}" > "${log_file_export_path}" gzip -c "${log_file_path}" > "${log_file_export_path}"
fi fi
else else
if [[ -f "${log_file_path}.old" ]]; then if [[ -f "${log_file_path}.old" ]]
then
cp "${log_file_path}.old" "${log_file_export_path}" cp "${log_file_path}.old" "${log_file_export_path}"
cat "${log_file_path}" >> "${log_file_export_path}" cat "${log_file_path}" >> "${log_file_export_path}"
else else
@ -272,8 +285,9 @@ export_log_file()
flush_log_fd() flush_log_fd()
{ {
log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}" log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
while read -r -t 0.01 -u "${log_fd}" log_line while read -r -t 0 -u "${log_fd}"
do do
read -r -u "${log_fd}" log_line
printf '%s\n' "${log_line}" >> "${log_file_path}" printf '%s\n' "${log_line}" >> "${log_file_path}"
done done
} }
@ -342,7 +356,7 @@ maintain_log_file()
done done
} }
get_next_shaper_rate() update_shaper_rate()
{ {
local direction="${1}" # 'dl' or 'ul' local direction="${1}" # 'dl' or 'ul'
@ -358,7 +372,8 @@ get_next_shaper_rate()
;; ;;
# bufferbloat detected, so decrease the rate providing not inside bufferbloat refractory period # bufferbloat detected, so decrease the rate providing not inside bufferbloat refractory period
*bb*) *bb*)
if (( t_start_us > (t_last_bufferbloat_us["${direction}"]+bufferbloat_refractory_period_us) )); then if (( t_start_us > (t_last_bufferbloat_us["${direction}"]+bufferbloat_refractory_period_us) ))
then
adjusted_achieved_rate_kbps=$(( (achieved_rate_kbps["${direction}"]*achieved_rate_adjust_down_bufferbloat)/1000 )) adjusted_achieved_rate_kbps=$(( (achieved_rate_kbps["${direction}"]*achieved_rate_adjust_down_bufferbloat)/1000 ))
adjusted_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_down_bufferbloat)/1000 )) adjusted_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_down_bufferbloat)/1000 ))
shaper_rate_kbps["${direction}"]=$(( adjusted_achieved_rate_kbps > min_shaper_rate_kbps["${direction}"] && adjusted_achieved_rate_kbps < adjusted_shaper_rate_kbps ? adjusted_achieved_rate_kbps : adjusted_shaper_rate_kbps )) shaper_rate_kbps["${direction}"]=$(( adjusted_achieved_rate_kbps > min_shaper_rate_kbps["${direction}"] && adjusted_achieved_rate_kbps < adjusted_shaper_rate_kbps ? adjusted_achieved_rate_kbps : adjusted_shaper_rate_kbps ))
@ -367,18 +382,22 @@ get_next_shaper_rate()
;; ;;
# high load, so increase rate providing not inside bufferbloat refractory period # high load, so increase rate providing not inside bufferbloat refractory period
*high*) *high*)
if (( t_start_us > (t_last_bufferbloat_us["${direction}"]+bufferbloat_refractory_period_us) )); then if (( t_start_us > (t_last_bufferbloat_us["${direction}"]+bufferbloat_refractory_period_us) ))
then
shaper_rate_kbps["${direction}"]=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_up_load_high)/1000 )) shaper_rate_kbps["${direction}"]=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_up_load_high)/1000 ))
fi fi
;; ;;
# low or idle load, so determine whether to decay down towards base rate, decay up towards base rate, or set as base rate # low or idle load, so determine whether to decay down towards base rate, decay up towards base rate, or set as base rate
*low*|*idle*) *low*|*idle*)
if (( t_start_us > (t_last_decay_us["${direction}"]+decay_refractory_period_us) )); then if (( t_start_us > (t_last_decay_us["${direction}"]+decay_refractory_period_us) ))
then
if ((shaper_rate_kbps["${direction}"] > base_shaper_rate_kbps["${direction}"])); then if ((shaper_rate_kbps["${direction}"] > base_shaper_rate_kbps["${direction}"]))
then
decayed_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_down_load_low)/1000 )) decayed_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_down_load_low)/1000 ))
shaper_rate_kbps["${direction}"]=$(( decayed_shaper_rate_kbps > base_shaper_rate_kbps["${direction}"] ? decayed_shaper_rate_kbps : base_shaper_rate_kbps["${direction}"])) shaper_rate_kbps["${direction}"]=$(( decayed_shaper_rate_kbps > base_shaper_rate_kbps["${direction}"] ? decayed_shaper_rate_kbps : base_shaper_rate_kbps["${direction}"]))
elif ((shaper_rate_kbps["${direction}"] < base_shaper_rate_kbps["${direction}"])); then elif ((shaper_rate_kbps["${direction}"] < base_shaper_rate_kbps["${direction}"]))
then
decayed_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_up_load_low)/1000 )) decayed_shaper_rate_kbps=$(( (shaper_rate_kbps["${direction}"]*shaper_rate_adjust_up_load_low)/1000 ))
shaper_rate_kbps["${direction}"]=$(( decayed_shaper_rate_kbps < base_shaper_rate_kbps["${direction}"] ? decayed_shaper_rate_kbps : base_shaper_rate_kbps["${direction}"])) shaper_rate_kbps["${direction}"]=$(( decayed_shaper_rate_kbps < base_shaper_rate_kbps["${direction}"] ? decayed_shaper_rate_kbps : base_shaper_rate_kbps["${direction}"]))
fi fi
@ -387,7 +406,7 @@ get_next_shaper_rate()
fi fi
;; ;;
*) *)
log_msg "ERROR" "unknown load condition: ${load_condition[${direction}]} in get_next_shaper_rate" log_msg "ERROR" "unknown load condition: ${load_condition[${direction}]} in update_shaper_rate"
kill $$ 2>/dev/null kill $$ 2>/dev/null
;; ;;
esac esac
@ -431,13 +450,13 @@ monitor_achieved_rates()
case "${command[0]:-}" in case "${command[0]:-}" in
SET_VAR) SET_VAR)
if [[ "${command[1]:-}" && "${command[2]:-}" ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
;; ;;
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
@ -457,8 +476,8 @@ monitor_achieved_rates()
[[ -f "${rx_bytes_path}" ]] && { read -r rx_bytes < "${rx_bytes_path}"; } 2> /dev/null || rx_bytes="${prev_rx_bytes}" [[ -f "${rx_bytes_path}" ]] && { read -r rx_bytes < "${rx_bytes_path}"; } 2> /dev/null || rx_bytes="${prev_rx_bytes}"
[[ -f "${tx_bytes_path}" ]] && { read -r tx_bytes < "${tx_bytes_path}"; } 2> /dev/null || tx_bytes="${prev_tx_bytes}" [[ -f "${tx_bytes_path}" ]] && { read -r tx_bytes < "${tx_bytes_path}"; } 2> /dev/null || tx_bytes="${prev_tx_bytes}"
achieved_rate_kbps[dl]=$(( ((8000*(rx_bytes - prev_rx_bytes)) / compensated_monitor_achieved_rates_interval_us ) )) achieved_rate_kbps[dl]=$(( (8000*(rx_bytes - prev_rx_bytes)) / compensated_monitor_achieved_rates_interval_us ))
achieved_rate_kbps[ul]=$(( ((8000*(tx_bytes - prev_tx_bytes)) / compensated_monitor_achieved_rates_interval_us ) )) achieved_rate_kbps[ul]=$(( (8000*(tx_bytes - prev_tx_bytes)) / compensated_monitor_achieved_rates_interval_us ))
((achieved_rate_kbps[dl]<0)) && achieved_rate_kbps[dl]=0 ((achieved_rate_kbps[dl]<0)) && achieved_rate_kbps[dl]=0
((achieved_rate_kbps[ul]<0)) && achieved_rate_kbps[ul]=0 ((achieved_rate_kbps[ul]<0)) && achieved_rate_kbps[ul]=0
@ -475,7 +494,8 @@ monitor_achieved_rates()
printf "SET_ARRAY_ELEMENT load_percent ul %s\n" "${load_percent[ul]}" >&"${pinger_fd}" printf "SET_ARRAY_ELEMENT load_percent ul %s\n" "${load_percent[ul]}" >&"${pinger_fd}"
done done
if ((output_load_stats)); then if ((output_load_stats))
then
printf -v load_stats '%s; %s; %s; %s; %s' "${EPOCHREALTIME}" "${achieved_rate_kbps[dl]}" "${achieved_rate_kbps[ul]}" "${shaper_rate_kbps[dl]}" "${shaper_rate_kbps[ul]}" printf -v load_stats '%s; %s; %s; %s; %s' "${EPOCHREALTIME}" "${achieved_rate_kbps[dl]}" "${achieved_rate_kbps[ul]}" "${shaper_rate_kbps[dl]}" "${shaper_rate_kbps[ul]}"
log_msg "LOAD" "${load_stats}" log_msg "LOAD" "${load_stats}"
@ -498,9 +518,11 @@ classify_load()
# thus ending up with high_delayed, low_delayed, etc. # thus ending up with high_delayed, low_delayed, etc.
local direction="${1}" local direction="${1}"
if (( load_percent["${direction}"] > high_load_thr_percent )); then if (( load_percent["${direction}"] > high_load_thr_percent ))
then
load_condition["${direction}"]="high" load_condition["${direction}"]="high"
elif (( achieved_rate_kbps["${direction}"] > connection_active_thr_kbps )); then elif (( achieved_rate_kbps["${direction}"] > connection_active_thr_kbps ))
then
load_condition["${direction}"]="low" load_condition["${direction}"]="low"
else else
load_condition["${direction}"]="idle" load_condition["${direction}"]="idle"
@ -508,12 +530,14 @@ classify_load()
((bufferbloat_detected["${direction}"])) && load_condition["${direction}"]="${load_condition[${direction}]}_bb" ((bufferbloat_detected["${direction}"])) && load_condition["${direction}"]="${load_condition[${direction}]}_bb"
if ((sss_compensation)); then if ((sss_compensation))
then
# shellcheck disable=SC2154 # shellcheck disable=SC2154
for sss_time_us in "${sss_times_us[@]}" for sss_time_us in "${sss_times_us[@]}"
do do
((timestamp_usecs_past_minute=${EPOCHREALTIME/./}%60000000)) ((timestamp_usecs_past_minute=${EPOCHREALTIME/./}%60000000))
if (( (timestamp_usecs_past_minute > (sss_time_us-sss_compensation_pre_duration_us)) && (timestamp_usecs_past_minute < (sss_time_us+sss_compensation_post_duration_us)) )); then if (( (timestamp_usecs_past_minute > (sss_time_us-sss_compensation_pre_duration_us)) && (timestamp_usecs_past_minute < (sss_time_us+sss_compensation_post_duration_us)) ))
then
load_condition["${direction}"]="${load_condition[${direction}]}_sss" load_condition["${direction}"]="${load_condition[${direction}]}_sss"
break break
fi fi
@ -609,7 +633,7 @@ parse_tsping()
SET_VAR) SET_VAR)
if [[ "${command[1]:-}" && "${command[2]:-}" ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
@ -618,7 +642,7 @@ parse_tsping()
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
@ -781,7 +805,7 @@ parse_fping()
SET_VAR) SET_VAR)
if [[ "${command[1]:-}" && "${command[2]:-}" ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
@ -790,7 +814,7 @@ parse_fping()
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
@ -914,7 +938,7 @@ parse_ping()
SET_REFLECTOR) SET_REFLECTOR)
if [[ "${command[1]:-}" ]] if [[ "${#command[@]}" -eq 2 ]]
then then
reflector="${command[1]}" reflector="${command[1]}"
log_msg "DEBUG" "Read in new reflector: ${reflector}" log_msg "DEBUG" "Read in new reflector: ${reflector}"
@ -928,7 +952,7 @@ parse_ping()
SET_VAR) SET_VAR)
if [[ "${command[1]:-}" && "${command[2]:-}" ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
@ -937,7 +961,7 @@ parse_ping()
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
@ -1034,8 +1058,8 @@ start_pinger()
start_pingers() start_pingers()
{ {
# Initiate pingers log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
log_msg "DEBUG" "Starting pingers."
case ${pinger_binary} in case ${pinger_binary} in
tsping|fping) tsping|fping)
@ -1079,7 +1103,6 @@ kill_pinger()
;; ;;
*) *)
:
;; ;;
esac esac
@ -1088,6 +1111,8 @@ kill_pinger()
kill_pingers() kill_pingers()
{ {
log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
case "${pinger_binary}" in case "${pinger_binary}" in
tsping|fping) tsping|fping)
@ -1121,7 +1146,8 @@ replace_pinger_reflector()
log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}" log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
if ((no_reflectors > no_pingers)); then if ((no_reflectors > no_pingers))
then
log_msg "DEBUG" "replacing reflector: ${reflectors[pinger]} with ${reflectors[no_pingers]}." log_msg "DEBUG" "replacing reflector: ${reflectors[pinger]} with ${reflectors[no_pingers]}."
kill_pinger "${pinger}" kill_pinger "${pinger}"
bad_reflector=${reflectors[pinger]} bad_reflector=${reflectors[pinger]}
@ -1198,13 +1224,14 @@ change_state_maintain_pingers()
START|STOP|PAUSED|RUNNING) START|STOP|PAUSED|RUNNING)
if [[ "${maintain_pingers_state}" != "${maintain_pingers_next_state}" ]] if [[ "${maintain_pingers_state}" == "${maintain_pingers_next_state}" ]]
then then
log_msg "ERROR" "Received request to change maintain_pingers state to existing state."
return
fi
log_msg "DEBUG" "Changing maintain_pingers state from: ${maintain_pingers_state} to: ${maintain_pingers_next_state}" log_msg "DEBUG" "Changing maintain_pingers state from: ${maintain_pingers_state} to: ${maintain_pingers_next_state}"
maintain_pingers_state=${maintain_pingers_next_state} maintain_pingers_state=${maintain_pingers_next_state}
else
log_msg "ERROR" "Received request to change maintain_pingers state to existing state."
fi
;; ;;
*) *)
@ -1292,7 +1319,7 @@ maintain_pingers()
case "${command[0]:-}" in case "${command[0]:-}" in
CHANGE_STATE) CHANGE_STATE)
if [[ "${command[1]:-}" ]] if [[ "${#command[@]}" -eq 2 ]]
then then
change_state_maintain_pingers "${command[1]}" change_state_maintain_pingers "${command[1]}"
# break out of reading any new IPC commands to handle next state # break out of reading any new IPC commands to handle next state
@ -1308,13 +1335,13 @@ maintain_pingers()
fi fi
;; ;;
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
;; ;;
SET_VAR) SET_VAR)
if [[ "${command[1]:-}" && "${command[2]:-}" ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
@ -1363,7 +1390,8 @@ maintain_pingers()
continue continue
fi fi
if (( t_start_us>(t_last_reflector_comparison_us+reflector_comparison_interval_mins*60*1000000) )); then if (( t_start_us>(t_last_reflector_comparison_us+reflector_comparison_interval_mins*60*1000000) ))
then
t_last_reflector_comparison_us=${EPOCHREALTIME/./} t_last_reflector_comparison_us=${EPOCHREALTIME/./}
@ -1431,16 +1459,19 @@ maintain_pingers()
# shellcheck disable=SC2154 # shellcheck disable=SC2154
reflector_offences[reflector_offences_idx]=$(( (${EPOCHREALTIME/./}-last_timestamp_reflectors_us[${reflectors[pinger]}]) > reflector_response_deadline_us ? 1 : 0 )) reflector_offences[reflector_offences_idx]=$(( (${EPOCHREALTIME/./}-last_timestamp_reflectors_us[${reflectors[pinger]}]) > reflector_response_deadline_us ? 1 : 0 ))
if (( reflector_offences[reflector_offences_idx] )); then if (( reflector_offences[reflector_offences_idx] ))
then
((sum_reflector_offences[pinger]++)) ((sum_reflector_offences[pinger]++))
log_msg "DEBUG" "no ping response from reflector: ${reflectors[pinger]} within reflector_response_deadline: ${reflector_response_deadline_s}s" log_msg "DEBUG" "no ping response from reflector: ${reflectors[pinger]} within reflector_response_deadline: ${reflector_response_deadline_s}s"
log_msg "DEBUG" "reflector=${reflectors[pinger]}, sum_reflector_offences=${sum_reflector_offences[pinger]} and reflector_misbehaving_detection_thr=${reflector_misbehaving_detection_thr}" log_msg "DEBUG" "reflector=${reflectors[pinger]}, sum_reflector_offences=${sum_reflector_offences[pinger]} and reflector_misbehaving_detection_thr=${reflector_misbehaving_detection_thr}"
fi fi
if (( sum_reflector_offences[pinger] >= reflector_misbehaving_detection_thr )); then if (( sum_reflector_offences[pinger] >= reflector_misbehaving_detection_thr ))
then
log_msg "DEBUG" "Warning: reflector: ${reflectors[pinger]} seems to be misbehaving." log_msg "DEBUG" "Warning: reflector: ${reflectors[pinger]} seems to be misbehaving."
if ((replace_pinger_reflector_enabled)); then if ((replace_pinger_reflector_enabled))
then
replace_pinger_reflector "${pinger}" replace_pinger_reflector "${pinger}"
replace_pinger_reflector_enabled=0 replace_pinger_reflector_enabled=0
else else
@ -1462,38 +1493,25 @@ maintain_pingers()
done done
} }
set_cake_rate() set_shaper_rate()
{ {
local interface="${1}" # fire up tc and update max_wire_packet_compensation if there are rates to change for the given direction
local shaper_rate_kbps="${2}"
local adjust_shaper_rate="${3}"
((output_cake_changes)) && log_msg "SHAPER" "tc qdisc change root dev ${interface} cake bandwidth ${shaper_rate_kbps}Kbit" local direction="${1}" # 'dl' or 'ul'
if ((adjust_shaper_rate)); then if (( shaper_rate_kbps["${direction}"] != last_shaper_rate_kbps["${direction}"] ))
then
tc qdisc change root dev "${interface}" cake bandwidth "${shaper_rate_kbps}Kbit" 2> /dev/null ((output_cake_changes)) && log_msg "SHAPER" "tc qdisc change root dev ${interface[${direction}]} cake bandwidth ${shaper_rate_kbps[${direction}]}Kbit"
if ((adjust_shaper_rate["${direction}"]))
then
tc qdisc change root dev "${interface[${direction}]}" cake bandwidth "${shaper_rate_kbps[${direction}]}Kbit" 2> /dev/null
else else
((output_cake_changes)) && log_msg "DEBUG" "adjust_shaper_rate set to 0 in config, so skipping the tc qdisc change call" ((output_cake_changes)) && log_msg "DEBUG" "adjust_${direction}_shaper_rate set to 0 in config, so skipping the corresponding tc qdisc change call."
fi fi
}
set_shaper_rates() printf "SET_ARRAY_ELEMENT shaper_rate_kbps ${direction} %s\n" "${shaper_rate_kbps[${direction}]}" >&"${monitor_achieved_rates_fd}"
{ last_shaper_rate_kbps["${direction}"]="${shaper_rate_kbps[${direction}]}"
if (( shaper_rate_kbps[dl] != last_shaper_rate_kbps[dl] || shaper_rate_kbps[ul] != last_shaper_rate_kbps[ul] )); then
# fire up tc in each direction if there are rates to change, and if rates change in either direction then update max wire calcs
if (( shaper_rate_kbps[dl] != last_shaper_rate_kbps[dl] )); then
set_cake_rate "${dl_if}" "${shaper_rate_kbps[dl]}" adjust_dl_shaper_rate
printf "SET_ARRAY_ELEMENT shaper_rate_kbps dl %s\n" "${shaper_rate_kbps[dl]}" >&"${monitor_achieved_rates_fd}"
last_shaper_rate_kbps[dl]="${shaper_rate_kbps[dl]}"
fi
if (( shaper_rate_kbps[ul] != last_shaper_rate_kbps[ul] )); then
set_cake_rate "${ul_if}" "${shaper_rate_kbps[ul]}" adjust_ul_shaper_rate
printf "SET_ARRAY_ELEMENT shaper_rate_kbps ul %s\n" "${shaper_rate_kbps[ul]}" >&"${monitor_achieved_rates_fd}"
last_shaper_rate_kbps[ul]="${shaper_rate_kbps[ul]}"
fi
update_max_wire_packet_compensation update_max_wire_packet_compensation
fi fi
@ -1504,7 +1522,8 @@ set_min_shaper_rates()
log_msg "DEBUG" "Enforcing minimum shaper rates." log_msg "DEBUG" "Enforcing minimum shaper rates."
shaper_rate_kbps[dl]=${min_dl_shaper_rate_kbps} shaper_rate_kbps[dl]=${min_dl_shaper_rate_kbps}
shaper_rate_kbps[ul]=${min_ul_shaper_rate_kbps} shaper_rate_kbps[ul]=${min_ul_shaper_rate_kbps}
set_shaper_rates set_shaper_rate "dl"
set_shaper_rate "ul"
} }
get_max_wire_packet_size_bits() get_max_wire_packet_size_bits()
@ -1564,6 +1583,8 @@ change_state_main()
{ {
local main_next_state="${1}" local main_next_state="${1}"
log_msg "DEBUG" "Starting: ${FUNCNAME[0]} with PID: ${BASHPID}"
case ${main_next_state} in case ${main_next_state} in
RUNNING|IDLE|STALL) RUNNING|IDLE|STALL)
@ -1624,7 +1645,8 @@ debug_cmd()
err_type="ERROR" err_type="ERROR"
if ((err_silence)); then if ((err_silence))
then
err_type="DEBUG" err_type="DEBUG"
fi fi
@ -1633,7 +1655,8 @@ debug_cmd()
caller_id=$(caller) caller_id=$(caller)
if ((ret==0)); then if ((ret==0))
then
log_msg "DEBUG" "debug_cmd: err_silence=${err_silence}; debug_msg=${debug_msg}; caller_id=${caller_id}; command=${cmd} ${args[*]}; result=SUCCESS" log_msg "DEBUG" "debug_cmd: err_silence=${err_silence}; debug_msg=${debug_msg}; caller_id=${caller_id}; command=${cmd} ${args[*]}; result=SUCCESS"
else else
[[ "${err_type}" == "DEBUG" && "${debug}" == "0" ]] && return # if debug disabled, then skip on DEBUG but not on ERROR [[ "${err_type}" == "DEBUG" && "${debug}" == "0" ]] && return # if debug disabled, then skip on DEBUG but not on ERROR
@ -1650,6 +1673,52 @@ debug_cmd()
fi fi
} }
# shellcheck disable=SC1090,SC2311
validate_config_entry() {
# Must be called before loading config_path into the global scope.
#
# When the entry is invalid, two types are returned with the first type
# being the invalid user type and second type is the default type with
# the user needing to adapt the config file so that the entry uses the
# default type.
#
# When the entry is valid, one type is returned and it will be the
# the type of either the default or user type. However because in that
# case they are both valid. It doesn't matter as they'd both have the
# same type.
local config_path="${1}"
local user_type
local valid_type
user_type=$(unset "${2}" && . "${config_path}" && typeof "${2}")
valid_type=$(typeof "${2}")
if [[ "${user_type}" != "${valid_type}" ]]
then
printf '%s' "${user_type} ${valid_type}"
return
elif [[ "${user_type}" != "string" ]]
then
printf '%s' "${valid_type}"
return
fi
# extra validation for string, check for empty string
local -n default_value=${2}
local user_value
user_value=$(. "${config_path}" && local -n x="${2}" && printf '%s' "${x}")
# if user is empty but default is not, invalid entry
if [[ -z "${user_value}" && -n "${default_value}" ]]
then
printf '%s' "${user_type} ${valid_type}"
else
printf '%s' "${valid_type}"
fi
}
# ======= Start of the Main Routine ======== # ======= Start of the Main Routine ========
[[ -t 1 ]] && terminal=1 || terminal=0 [[ -t 1 ]] && terminal=1 || terminal=0
@ -1663,35 +1732,78 @@ log_file_path=/var/log/cake-autorate.log
run_path=/var/run/cake-autorate/ run_path=/var/run/cake-autorate/
# cake-autorate first argument is config file path # cake-autorate first argument is config file path
if [[ -n ${1-} ]]; then if [[ -n "${1-}" ]]
then
config_path="${1}" config_path="${1}"
else else
config_path="${PREFIX}/cake-autorate_config.primary.sh" config_path="${PREFIX}/config.primary.sh"
fi fi
if [[ ! -f "${config_path}" ]]; then if [[ ! -f "${config_path}" ]]
then
log_msg "ERROR" "No config file found. Exiting now." log_msg "ERROR" "No config file found. Exiting now."
exit exit 1
fi fi
# shellcheck source=cake-autorate_config.primary.sh # validate config entries before loading
mapfile -t user_config < <(grep -E '^[^(#| )].*=' "${config_path}" | sed -e 's/[\t ]*\#.*//g' -e 's/=.*//g')
config_error_count=0
for key in "${user_config[@]}"
do
# Despite the fact that config_file_check is no longer required,
# we make an exemption just in this case as that variable in
# particular does not have any real impact to the operation
# of the script.
[[ "${key}" == "config_file_check" ]] && continue
# shellcheck disable=SC2076
if [[ ! " ${valid_config_entries[*]} " =~ " ${key} " ]]
then
((config_error_count++))
log_msg "ERROR" "The key: '${key}' in config file: '${config_path}' is not a valid config entry."
else
# shellcheck disable=SC2311
read -r user supposed <<< "$(validate_config_entry "${config_path}" "${key}")"
if [[ -n "${supposed}" ]]
then
error_msg="The value of '${key}' in config file: '${config_path}' is not a valid value of type: '${supposed}'."
case "${user}" in
negative-*) error_msg="${error_msg} Also, negative numbers are not supported." ;;
*) ;;
esac
log_msg "ERROR" "${error_msg}"
unset error_msg
((config_error_count++))
fi
unset user supposed
fi
done
if ((config_error_count))
then
log_msg "ERROR" "The config file: '${config_path}' contains ${config_error_count} error(s). Exiting now."
exit 1
fi
unset valid_config_entries user_config config_error_count key
# shellcheck source=config.primary.sh
. "${config_path}" . "${config_path}"
if [[ ${config_file_check} != "cake-autorate" ]]; then if [[ ${config_path} =~ config\.(.*)\.sh ]]
log_msg "ERROR" "Config file error. Please check config file entries." then
exit instance_id="${BASH_REMATCH[1]}"
fi run_path="/var/run/cake-autorate/${instance_id}"
if [[ ${config_path} =~ cake-autorate_config\.(.*)\.sh ]]; then
instance_id=${BASH_REMATCH[1]}
run_path=/var/run/cake-autorate/${instance_id}
else else
log_msg "ERROR" "Instance identifier 'X' set by cake-autorate_config.X.sh cannot be empty. Exiting now." log_msg "ERROR" "Instance identifier 'X' set by config.X.sh cannot be empty. Exiting now."
exit exit 1
fi fi
if [[ -n "${log_file_path_override-}" ]]; then if [[ -n "${log_file_path_override-}" ]]
if [[ ! -d ${log_file_path_override} ]]; then then
if [[ ! -d ${log_file_path_override} ]]
then
broken_log_file_path_override=${log_file_path_override} broken_log_file_path_override=${log_file_path_override}
log_file_path=/var/log/cake-autorate${instance_id:+.${instance_id}}.log log_file_path=/var/log/cake-autorate${instance_id:+.${instance_id}}.log
log_msg "ERROR" "Log file path override: '${broken_log_file_path_override}' does not exist. Exiting now." log_msg "ERROR" "Log file path override: '${broken_log_file_path_override}' does not exist. Exiting now."
@ -1714,7 +1826,8 @@ log_msg "SYSLOG" "Starting cake-autorate with PID: ${BASHPID} and config: ${conf
# ${run_path}/ is used to store temporary files # ${run_path}/ is used to store temporary files
# it should not exist on startup so if it does exit, else create the directory # it should not exist on startup so if it does exit, else create the directory
if [[ -d "${run_path}" ]]; then if [[ -d "${run_path}" ]]
then
if [[ -f "${run_path}/proc_pids" ]] && running_main_pid=$(awk -F= '/^main=/ {print $2}' "${run_path}/proc_pids") && [[ -d "/proc/${running_main_pid}" ]] if [[ -f "${run_path}/proc_pids" ]] && running_main_pid=$(awk -F= '/^main=/ {print $2}' "${run_path}/proc_pids") && [[ -d "/proc/${running_main_pid}" ]]
then then
log_msg "ERROR" "${run_path} already exists and an instance appears to be running with main process pid ${running_main_pid}. Exiting script." log_msg "ERROR" "${run_path} already exists and an instance appears to be running with main process pid ${running_main_pid}. Exiting script."
@ -1747,7 +1860,8 @@ command -v "${pinger_binary}" &> /dev/null || { log_msg "ERROR" "ping binary ${p
# Passed error checks # Passed error checks
if ((log_to_file)); then if ((log_to_file))
then
log_file_max_time_us=$((log_file_max_time_mins*60000000)) log_file_max_time_us=$((log_file_max_time_mins*60000000))
log_file_max_size_bytes=$((log_file_max_size_KB*1024)) log_file_max_size_bytes=$((log_file_max_size_KB*1024))
exec {log_fd}<> <(:) exec {log_fd}<> <(:)
@ -1756,13 +1870,15 @@ if ((log_to_file)); then
fi fi
# test if stdout is a tty (terminal) # test if stdout is a tty (terminal)
if ! ((terminal)); then if ! ((terminal))
then
echo "stdout not a terminal so redirecting output to: ${log_file_path}" echo "stdout not a terminal so redirecting output to: ${log_file_path}"
((log_to_file)) && exec 1>&"${log_fd}" ((log_to_file)) && exec 1>&"${log_fd}"
fi fi
# Initialize rx_bytes_path and tx_bytes_path if not set # Initialize rx_bytes_path and tx_bytes_path if not set
if [[ -z "${rx_bytes_path-}" ]]; then if [[ -z "${rx_bytes_path-}" ]]
then
case "${dl_if}" in case "${dl_if}" in
veth*) veth*)
rx_bytes_path="/sys/class/net/${dl_if}/statistics/tx_bytes" rx_bytes_path="/sys/class/net/${dl_if}/statistics/tx_bytes"
@ -1775,7 +1891,8 @@ if [[ -z "${rx_bytes_path-}" ]]; then
;; ;;
esac esac
fi fi
if [[ -z "${tx_bytes_path-}" ]]; then if [[ -z "${tx_bytes_path-}" ]]
then
case "${ul_if}" in case "${ul_if}" in
veth*) veth*)
tx_bytes_path="/sys/class/net/${ul_if}/statistics/rx_bytes" tx_bytes_path="/sys/class/net/${ul_if}/statistics/rx_bytes"
@ -1789,7 +1906,8 @@ if [[ -z "${tx_bytes_path-}" ]]; then
esac esac
fi fi
if ((debug)) ; then if ((debug))
then
log_msg "DEBUG" "CAKE-autorate version: ${cake_autorate_version}" log_msg "DEBUG" "CAKE-autorate version: ${cake_autorate_version}"
log_msg "DEBUG" "config_path: ${config_path}" log_msg "DEBUG" "config_path: ${config_path}"
log_msg "DEBUG" "run_path: ${run_path}" log_msg "DEBUG" "run_path: ${run_path}"
@ -1856,6 +1974,8 @@ declare -A last_shaper_rate_kbps
declare -A base_shaper_rate_kbps declare -A base_shaper_rate_kbps
declare -A min_shaper_rate_kbps declare -A min_shaper_rate_kbps
declare -A max_shaper_rate_kbps declare -A max_shaper_rate_kbps
declare -A interface
declare -A adjust_shaper_rate
base_shaper_rate_kbps[dl]="${base_dl_shaper_rate_kbps}" base_shaper_rate_kbps[dl]="${base_dl_shaper_rate_kbps}"
base_shaper_rate_kbps[ul]="${base_ul_shaper_rate_kbps}" base_shaper_rate_kbps[ul]="${base_ul_shaper_rate_kbps}"
@ -1872,10 +1992,19 @@ shaper_rate_kbps[ul]="${base_ul_shaper_rate_kbps}"
last_shaper_rate_kbps[dl]=0 last_shaper_rate_kbps[dl]=0
last_shaper_rate_kbps[ul]=0 last_shaper_rate_kbps[ul]=0
interface[dl]="${dl_if}"
interface[ul]="${ul_if}"
adjust_shaper_rate[dl]="${adjust_dl_shaper_rate}"
adjust_shaper_rate[ul]="${adjust_ul_shaper_rate}"
dl_max_wire_packet_size_bits=0
ul_max_wire_packet_size_bits=0
get_max_wire_packet_size_bits "${dl_if}" dl_max_wire_packet_size_bits get_max_wire_packet_size_bits "${dl_if}" dl_max_wire_packet_size_bits
get_max_wire_packet_size_bits "${ul_if}" ul_max_wire_packet_size_bits get_max_wire_packet_size_bits "${ul_if}" ul_max_wire_packet_size_bits
set_shaper_rates set_shaper_rate "dl"
set_shaper_rate "ul"
update_max_wire_packet_compensation update_max_wire_packet_compensation
@ -1899,13 +2028,16 @@ delays_idx=0
sum_dl_delays=0 sum_dl_delays=0
sum_ul_delays=0 sum_ul_delays=0
if ((debug)); then if ((debug))
if (( bufferbloat_refractory_period_us < (bufferbloat_detection_window*ping_response_interval_us) )); then then
if (( bufferbloat_refractory_period_us < (bufferbloat_detection_window*ping_response_interval_us) ))
then
log_msg "DEBUG" "Warning: bufferbloat refractory period: ${bufferbloat_refractory_period_us} us." log_msg "DEBUG" "Warning: bufferbloat refractory period: ${bufferbloat_refractory_period_us} us."
log_msg "DEBUG" "Warning: but expected time to overwrite samples in bufferbloat detection window is: $((bufferbloat_detection_window*ping_response_interval_us)) us." log_msg "DEBUG" "Warning: but expected time to overwrite samples in bufferbloat detection window is: $((bufferbloat_detection_window*ping_response_interval_us)) us."
log_msg "DEBUG" "Warning: Consider increasing bufferbloat refractory period or decreasing bufferbloat detection window." log_msg "DEBUG" "Warning: Consider increasing bufferbloat refractory period or decreasing bufferbloat detection window."
fi fi
if (( reflector_response_deadline_us < 2*reflector_ping_interval_us )); then if (( reflector_response_deadline_us < 2*reflector_ping_interval_us ))
then
log_msg "DEBUG" "Warning: reflector_response_deadline_s < 2*reflector_ping_interval_s" log_msg "DEBUG" "Warning: reflector_response_deadline_s < 2*reflector_ping_interval_s"
log_msg "DEBUG" "Warning: consider setting an increased reflector_response_deadline." log_msg "DEBUG" "Warning: consider setting an increased reflector_response_deadline."
fi fi
@ -1915,7 +2047,8 @@ fi
((randomize_reflectors)) && randomize_array reflectors ((randomize_reflectors)) && randomize_array reflectors
# Wait if ${startup_wait_s} > 0 # Wait if ${startup_wait_s} > 0
if ((startup_wait_us>0)); then if ((startup_wait_us>0))
then
log_msg "DEBUG" "Waiting ${startup_wait_s} seconds before startup." log_msg "DEBUG" "Waiting ${startup_wait_s} seconds before startup."
sleep_us "${startup_wait_us}" sleep_us "${startup_wait_us}"
fi fi
@ -1925,6 +2058,7 @@ case "${pinger_binary}" in
tsping|fping) tsping|fping)
exec {pinger_fds[0]}<> <(:) exec {pinger_fds[0]}<> <(:)
;; ;;
ping) ping)
for ((pinger=0; pinger<=no_pingers; pinger++)) for ((pinger=0; pinger<=no_pingers; pinger++))
do do
@ -1934,7 +2068,7 @@ case "${pinger_binary}" in
*) *)
log_msg "ERROR" "Unknown pinger binary: ${pinger_binary}" log_msg "ERROR" "Unknown pinger binary: ${pinger_binary}"
kill $$ 2>/dev/null exit
;; ;;
esac esac
@ -1964,19 +2098,19 @@ do
;; ;;
SET_VAR) SET_VAR)
if [[ ${command[1]:-} && ${command[2]:-} ]] if [[ "${#command[@]}" -eq 3 ]]
then then
export -n "${command[1]}=${command[2]}" export -n "${command[1]}=${command[2]}"
fi fi
;; ;;
SET_ARRAY_ELEMENT) SET_ARRAY_ELEMENT)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
;; ;;
SET_PROC_PID) SET_PROC_PID)
if [[ "${command[1]:-}" && "${command[2]:-}" && "${command[3]:-}" ]] if [[ "${#command[@]}" -eq 4 ]]
then then
declare -A "${command[1]}"+="([${command[2]}]=${command[3]})" declare -A "${command[1]}"+="([${command[2]}]=${command[3]})"
fi fi
@ -2002,7 +2136,8 @@ do
reflectors_last_timestamp_us="${timestamp//[.]}" reflectors_last_timestamp_us="${timestamp//[.]}"
if (( (t_start_us - 10#"${reflectors_last_timestamp_us}")>500000 )); then if (( (t_start_us - 10#"${reflectors_last_timestamp_us}")>500000 ))
then
log_msg "DEBUG" "processed response from [${reflector}] that is > 500ms old. Skipping." log_msg "DEBUG" "processed response from [${reflector}] that is > 500ms old. Skipping."
continue continue
fi fi
@ -2028,19 +2163,23 @@ do
classify_load "dl" classify_load "dl"
classify_load "ul" classify_load "ul"
get_next_shaper_rate "dl" update_shaper_rate "dl"
get_next_shaper_rate "ul" update_shaper_rate "ul"
set_shaper_rates set_shaper_rate "dl"
set_shaper_rate "ul"
if (( output_processing_stats )); then if (( output_processing_stats ))
then
printf -v processing_stats '%s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s' "${EPOCHREALTIME}" "${achieved_rate_kbps[dl]}" "${achieved_rate_kbps[ul]}" "${load_percent[dl]}" "${load_percent[ul]}" "${timestamp}" "${reflector}" "${seq}" "${dl_owd_baseline_us}" "${dl_owd_us}" "${dl_owd_delta_ewma_us}" "${dl_owd_delta_us}" "${compensated_dl_delay_thr_us}" "${ul_owd_baseline_us}" "${ul_owd_us}" "${ul_owd_delta_ewma_us}" "${ul_owd_delta_us}" "${compensated_ul_delay_thr_us}" "${sum_dl_delays}" "${sum_ul_delays}" "${load_condition[dl]}" "${load_condition[ul]}" "${shaper_rate_kbps[dl]}" "${shaper_rate_kbps[ul]}" printf -v processing_stats '%s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s; %s' "${EPOCHREALTIME}" "${achieved_rate_kbps[dl]}" "${achieved_rate_kbps[ul]}" "${load_percent[dl]}" "${load_percent[ul]}" "${timestamp}" "${reflector}" "${seq}" "${dl_owd_baseline_us}" "${dl_owd_us}" "${dl_owd_delta_ewma_us}" "${dl_owd_delta_us}" "${compensated_dl_delay_thr_us}" "${ul_owd_baseline_us}" "${ul_owd_us}" "${ul_owd_delta_ewma_us}" "${ul_owd_delta_us}" "${compensated_ul_delay_thr_us}" "${sum_dl_delays}" "${sum_ul_delays}" "${load_condition[dl]}" "${load_condition[ul]}" "${shaper_rate_kbps[dl]}" "${shaper_rate_kbps[ul]}"
log_msg "DATA" "${processing_stats}" log_msg "DATA" "${processing_stats}"
fi fi
# If base rate is sustained, increment sustained base rate timer (and break out of processing loop if enough time passes) # If base rate is sustained, increment sustained base rate timer (and break out of processing loop if enough time passes)
if (( enable_sleep_function )); then if (( enable_sleep_function ))
if [[ ${load_condition[dl]} == *idle* && ${load_condition[ul]} == *idle* ]]; then then
if [[ ${load_condition[dl]} == *idle* && ${load_condition[ul]} == *idle* ]]
then
((t_sustained_connection_idle_us += (${EPOCHREALTIME/./}-t_end_us) )) ((t_sustained_connection_idle_us += (${EPOCHREALTIME/./}-t_end_us) ))
if ((t_sustained_connection_idle_us > sustained_idle_sleep_thr_us)) if ((t_sustained_connection_idle_us > sustained_idle_sleep_thr_us))
then then
@ -2124,7 +2263,7 @@ do
*) *)
log_msg "ERROR" "Unrecognized main state: ${main_state}. Exiting now." log_msg "ERROR" "Unrecognized main state: ${main_state}. Exiting now."
kill $$ 2>/dev/null exit 1
;; ;;
esac esac

View file

@ -9,7 +9,7 @@
INTERFACE=$(basename "$1" | cut -d. -f2) INTERFACE=$(basename "$1" | cut -d. -f2)
cake_autorate_version="2.0.0" #cake_autorate_version="2.0.0"
# *** OUTPUT AND LOGGING OPTIONS *** # *** OUTPUT AND LOGGING OPTIONS ***
@ -46,7 +46,7 @@ ul_if=$(uci -q get sqm.${INTERFACE}.interface) # upload interface
# fping - round robin pinging (rtts) # fping - round robin pinging (rtts)
# ping - (iputils-ping) individual pinging (rtts) # ping - (iputils-ping) individual pinging (rtts)
# hping3 - individidual pinging (owds) # hping3 - individidual pinging (owds)
pinger_binary=tsping pinger_binary=$(uci -q get sqm.${INTERFACE}.pinger || echo 'tsping')
# list of reflectors to use and number of pingers to initiate # list of reflectors to use and number of pingers to initiate
# pingers will be initiated with reflectors in the order specified in the list # pingers will be initiated with reflectors in the order specified in the list
@ -77,12 +77,8 @@ reflector_ping_interval_s=$(uci -q get sqm.${INTERFACE}.reflector_ping_interval_
# delay threshold in ms is the extent of OWD increase to classify as a delay # delay threshold in ms is the extent of OWD increase to classify as a delay
# these are automatically adjusted based on maximum on the wire packet size # these are automatically adjusted based on maximum on the wire packet size
# (adjustment significant at sub 12Mbit/s rates, else negligible) # (adjustment significant at sub 12Mbit/s rates, else negligible)
latency=$(uci -q get sqm.${INTERFACE}.delay_thr_ms) dl_delay_thr_ms=$(uci -q get sqm.${INTERFACE}.delay_thr_ms) || $(($(ping -B -w 5 -c 5 -I ${ul_if} 1.1.1.1 | cut -d '/' -s -f6 | cut -d '.' -f1 | tr -d '\n' 2>/dev/null)+30)) || echo 100 # (milliseconds)
[ -z "$latency" ] && latency="$(($(ping -B -w 5 -c 5 -I ${ul_if} 1.1.1.1 | cut -d '/' -s -f6 | cut -d '.' -f1 | tr -d '\n' 2>/dev/null)+30))" ul_delay_thr_ms=${dl_delay_thr_ms}
[ -z "$latency" ] && latency="100"
logger -t "sqm" "latency $INTERFACE: $latency"
dl_delay_thr_ms="$latency" # (milliseconds)
ul_delay_thr_ms="$latency" # (milliseconds)
# Set either of the below to 0 to adjust one direction only # Set either of the below to 0 to adjust one direction only
# or alternatively set both to 0 to simply use cake-autorate to monitor a connection # or alternatively set both to 0 to simply use cake-autorate to monitor a connection
@ -195,7 +191,7 @@ reflector_misbehaving_detection_thr=3
reflector_replacement_interval_mins=60 # how often to replace a random reflector from the present list reflector_replacement_interval_mins=60 # how often to replace a random reflector from the present list
reflector_comparison_interval_mins=1 # how often to compare reflectors reflector_comparison_interval_mins=1 # how often to compare reflectors
reflector_sum_owd_baseline_delta_thr_ms=30 # max increase from min sum owd baselines before reflector rotated #reflector_sum_owd_baseline_delta_thr_ms=30 # max increase from min sum owd baselines before reflector rotated
reflector_owd_delta_ewma_delta_thr_ms=10 # mac increase from min delta ewma before reflector rotated reflector_owd_delta_ewma_delta_thr_ms=10 # mac increase from min delta ewma before reflector rotated
# stall is detected when the following two conditions are met: # stall is detected when the following two conditions are met:

View file

@ -7,6 +7,8 @@
# Author: @Lynx (OpenWrt forum) # Author: @Lynx (OpenWrt forum)
# Inspiration taken from: @moeller0 (OpenWrt forum) # Inspiration taken from: @moeller0 (OpenWrt forum)
INTERFACE=""
# *** OUTPUT AND LOGGING OPTIONS *** # *** OUTPUT AND LOGGING OPTIONS ***
output_processing_stats=1 # enable (1) or disable (0) output monitoring lines showing processing stats output_processing_stats=1 # enable (1) or disable (0) output monitoring lines showing processing stats

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
cake_instances=(/usr/share/sqm-autorate/cake-autorate_config*sh) cake_instances=(/root/cake-autorate/config.*.sh)
cake_instance_pids=() cake_instance_pids=()
trap kill_cake_instances INT TERM EXIT trap kill_cake_instances INT TERM EXIT
@ -20,7 +20,7 @@ kill_cake_instances()
for cake_instance in "${cake_instances[@]}" for cake_instance in "${cake_instances[@]}"
do do
/usr/share/sqm-autorate/cake-autorate.sh "${cake_instance}" & /root/cake-autorate/cake-autorate.sh "${cake_instance}" &
cake_instance_pids+=(${!}) cake_instance_pids+=(${!})
done done
wait wait

View file

@ -1,14 +1,63 @@
#!/bin/bash #!/bin/bash
# cake-autorate_lib.sh -- common functions for use by cake-autorate.sh
# lib.sh -- common functions for use by cake-autorate.sh
#
# This file is part of cake-autorate. # This file is part of cake-autorate.
__set_e=0 __set_e=0
if [[ ! ${-} =~ e ]]; then if [[ ! ${-} =~ e ]]
then
set -e set -e
__set_e=1 __set_e=1
fi fi
exec {__sleep_fd}<> <(:) || true if [[ -z "${__sleep_fd:-}" ]]
then
exec {__sleep_fd}<> <(:)
fi
typeof() {
# typeof -- returns the type of a variable
local type_sig
type_sig=$(declare -p "${1}" 2>/dev/null)
if [[ "${type_sig}" =~ "declare --" ]]
then
str_type "${1}"
elif [[ "${type_sig}" =~ "declare -a" ]]
then
printf "array"
elif [[ "${type_sig}" =~ "declare -A" ]]
then
printf "map"
else
printf "none"
fi
}
str_type() {
# str_type -- returns the type of a string
local -n str="${1}"
if [[ "${str}" =~ ^[0-9]+$ ]]
then
printf "integer"
elif [[ "${str}" =~ ^[0-9]*\.[0-9]+$ ]]
then
printf "float"
elif [[ "${str}" =~ ^-[0-9]+$ ]]
then
printf "negative-integer"
elif [[ "${str}" =~ ^-[0-9]*\.[0-9]+$ ]]
then
printf "negative-float"
else
# technically not validated, user is just trusted to call
# this function with valid strings
printf "string"
fi
}
sleep_s() sleep_s()
{ {
@ -25,7 +74,7 @@ sleep_s()
# - https://github.com/lynxthecat/cake-autorate/issues/174#issuecomment-1460074498 # - https://github.com/lynxthecat/cake-autorate/issues/174#issuecomment-1460074498
local sleep_duration_s=${1} # (seconds, e.g. 0.5, 1 or 1.5) local sleep_duration_s=${1} # (seconds, e.g. 0.5, 1 or 1.5)
read -r -t "${sleep_duration_s}" -u "${__sleep_fd}" || : read -r -t "${sleep_duration_s}" -u "${__sleep_fd}" || true
} }
sleep_us() sleep_us()
@ -52,21 +101,10 @@ sleep_remaining_tick_time()
fi fi
} }
get_remaining_tick_time()
{
# updates sleep_duration_s remaining to end of tick duration
local t_start_us=${1} # (microseconds)
local tick_duration_us=${2} # (microseconds)
sleep_duration_us=$(( t_start_us + tick_duration_us - ${EPOCHREALTIME/./} ))
((sleep_duration_us<0)) && sleep_duration_us=0
sleep_duration_s=000000${sleep_duration_us}
sleep_duration_s=$((10#${sleep_duration_s::-6})).${sleep_duration_s: -6}
}
randomize_array() randomize_array()
{ {
# randomize the order of the elements of an array
local -n array=${1} local -n array=${1}
subset=("${array[@]}") subset=("${array[@]}")
@ -80,23 +118,6 @@ randomize_array()
done done
} }
lock()
{
local path=${1}
while true; do
( set -o noclobber; echo "$$" > "${path:?}" ) 2> /dev/null && return 0
sleep_us 100000
done
}
unlock()
{
local path=${1}
rm -f "${path:?}"
}
terminate() terminate()
{ {
# Send regular kill to processes and monitor terminations; # Send regular kill to processes and monitor terminations;
@ -121,8 +142,8 @@ terminate()
kill -9 "${pids[@]}" 2> /dev/null kill -9 "${pids[@]}" 2> /dev/null
} }
if (( __set_e == 1 ))
if (( __set_e == 1 )); then then
set +e set +e
fi fi
unset __set_e unset __set_e