#!/bin/sh # CakeQOS-Merlin - port for Merlin firmware supported routers # Site: https://github.com/ttgapers/cakeqos-merlin # Thread: https://www.snbforums.com/threads/release-cakeqos-merlin.64800/ # Credits: robcore, Odkrys, ttgapers, jackiechun, maghuro, Adamm, Jack Yaz, dave14305 ######################################################### ## _ ## ## | | ## ## ___ __ _ | | __ ___ __ _ ___ ___ ## ## / __|/ _` || |/ // _ \ ______ / _` | / _ \ / __| ## ## | (__ |(_| || <| __/|______| (_| || (_) |\__ \ ## ## \___|\__,_||_|\_\\___| \__, | \___/ |___/ ## ## | | ## ## |_| ## ## ## ## https://github.com/ttgapers/cakeqos-merlin ## ## v2.1.2 ## ## ## ######################################################### # shellcheck disable=SC2086 version=2.1.2 readonly SCRIPT_NAME="cake-qos" readonly SCRIPT_NAME_FANCY="CakeQOS-Merlin" readonly SCRIPT_BRANCH="master" readonly SCRIPT_DIR="/jffs/addons/${SCRIPT_NAME}" readonly SCRIPT_REMOTEDIR="https://raw.githubusercontent.com/ttgapers/cakeqos-merlin/${SCRIPT_BRANCH}" IPv6_enabled="$(nvram get ipv6_service)" #readonly CRIT="\\e[41m" readonly ERR="\\e[31m" readonly WARN="\\e[33m" readonly PASS="\\e[32m" [ -z "$(nvram get odmpid)" ] && RMODEL=$(nvram get productid) || RMODEL=$(nvram get odmpid) #get router model # shellcheck disable=SC1091 . /usr/sbin/helper.sh # Update version number in custom_settings.txt for reading in WebUI if [ "$(am_settings_get cakeqos_ver)" != "$version" ]; then am_settings_set cakeqos_ver "$version" fi # Detect if script is run from an SSH shell interactively or being invoked via cron or from the WebUI (unattended) if tty >/dev/null 2>&1; then mode="interactive" clear sed -n '6,21p' "$0" else mode="unattended" fi Print_Output(){ if [ "$1" = "true" ]; then logger -t "$SCRIPT_NAME_FANCY" "$2" printf "\\e[1m$3%s: $2\\e[0m\\n" "$SCRIPT_NAME_FANCY" else printf "\\e[1m$3%s: $2\\e[0m\\n" "$SCRIPT_NAME_FANCY" fi } get_wanif() { prefixes="wan0_ wan1_" if [ "$(nvram get wans_mode)" = "lb" ] ; then for prefix in $prefixes; do state=$(nvram get "${prefix}"state_t) sbstate=$(nvram get "${prefix}"sbstate_t) auxstate=$(nvram get "${prefix}"auxstate_t) # is_wan_connect() [ "$state" = "2" ] || continue [ "$sbstate" = "0" ] || continue [ "$auxstate" = "0" ] || [ "$auxstate" = "2" ] || continue # get_wan_ifname() proto=$(nvram get "${prefix}"proto) if [ "$proto" = "pppoe" ] || [ "$proto" = "pptp" ] || [ "$proto" = "l2tp" ] ; then ifname=$(nvram get "${prefix}"pppoe_ifname) else ifname=$(nvram get "${prefix}"ifname) fi done else for prefix in $prefixes; do primary=$(nvram get "${prefix}"primary) [ "$primary" = "1" ] && break done [ "$primary" = "1" ] || ifname="eth0" # get_wan_ifname() proto=$(nvram get "${prefix}"proto) if [ "$proto" = "pppoe" ] || [ "$proto" = "pptp" ] || [ "$proto" = "l2tp" ] ; then ifname=$(nvram get "${prefix}"pppoe_ifname) else ifname=$(nvram get "${prefix}"ifname) fi fi printf "%s" "$ifname" } Cake_Get_Prio(){ DIR="$1" PRIO="$(am_settings_get cakeqos_${DIR}prio)" if [ -z "$PRIO" ]; then case $DIR in 'dl') PRIO="3" ;; 'ul') PRIO="0" ;; esac fi case $PRIO in 0) printf "diffserv3\n" ;; 1) printf "diffserv4\n" ;; 2) printf "diffserv8\n" ;; 3) printf "besteffort\n" ;; *) printf "\n" ;; esac } Cake_Get_FlowIso(){ DIR="$1" FLOWISO="$(am_settings_get cakeqos_${DIR}flowiso)" if [ -z "$FLOWISO" ]; then case $DIR in 'dl') FLOWISO="6" ;; 'ul') FLOWISO="5" ;; esac fi case $FLOWISO in 0) printf "flowblind\n" ;; 1) printf "srchost\n" ;; 2) printf "dsthost\n" ;; 3) printf "hosts\n" ;; 4) printf "flows\n" ;; 5) printf "dual-srchost\n" ;; 6) printf "dual-dsthost\n" ;; 7) printf "triple-isolate\n" ;; *) printf "\n" ;; esac } Cake_Get_NAT(){ DIR="$1" NAT="$(am_settings_get cakeqos_${DIR}nat)" [ -z "$NAT" ] && NAT="$(nvram get wan0_nat_x)" case $NAT in 0) printf "nonat\n" ;; 1) printf "nat\n" ;; *) printf "\n" ;; esac } Cake_Get_Wash(){ DIR="$1" WASH="$(am_settings_get cakeqos_${DIR}wash)" if [ -z "$WASH" ]; then case $DIR in 'dl') WASH="1" ;; 'ul') WASH="0" ;; esac fi case $WASH in 0) printf "nowash\n" ;; 1) printf "wash\n" ;; *) printf "\n" ;; esac } Cake_Get_ACK(){ DIR="$1" ACK="$(am_settings_get cakeqos_${DIR}ack)" [ -z "$ACK" ] && ACK="0" case $ACK in 0) printf "no-ack-filter\n" ;; 1) printf "ack-filter\n" ;; *) printf "\n" ;; esac } Cake_Get_CustomOpts(){ DIR="$1" CUST="$(am_settings_get cakeqos_${DIR}cust | /usr/sbin/openssl enc -a -d)" [ -z "$CUST" ] || printf "%s\n" "$CUST" } Cake_Write_QOS(){ if [ "$(nvram get qos_enable)" != "1" ] || [ "$(nvram get qos_type)" != "9" ]; then logger -t "$SCRIPT_NAME_FANCY" "Cake QoS not enabled in firmware. Skipping configuration." return 1 fi logger -t "$SCRIPT_NAME_FANCY" "Configuring Cake options..." cat >/jffs/configs/cake-qos.conf.add < /www/ext/${SCRIPT_NAME}/cake_status.js } Download_File() { if [ "$(curl -fsL --retry 3 --connect-timeout 3 "${SCRIPT_REMOTEDIR}/${1}" | md5sum | awk '{print $1}')" != "$(md5sum "$2" 2>/dev/null | awk '{print $1}')" ]; then if curl -fsL --retry 3 --connect-timeout 3 "${SCRIPT_REMOTEDIR}/${1}" -o "$2"; then Print_Output "false" "Downloaded $(echo "$1" | awk -F / '{print $NF}')" "$PASS" else Print_Output "false" "Downloading $(echo "$1" | awk -F / '{print $NF}') Failed" "$ERR" return 1 fi else return 1 fi } Cake_Mount_UI(){ # Check if the webpage is already mounted in the GUI and reuse that page LOCKFILE=/tmp/addonwebui.lock FD=386 eval exec "$FD>$LOCKFILE" /usr/bin/flock -x "$FD" prev_webui_page="$(sed -nE "s/^\{url\: \"(user[0-9]+\.asp)\"\, tabName\: \"${SCRIPT_NAME_FANCY}\"\}\,$/\1/p" /tmp/menuTree.js 2>/dev/null)" if [ -n "$prev_webui_page" ]; then # use the same filename as before am_webui_page="$prev_webui_page" else # get a new mountpoint am_get_webui_page "${SCRIPT_DIR}/${SCRIPT_NAME}.asp" fi if [ "$am_webui_page" = "none" ]; then Print_Output "true" "No API slots available to install web page" else cp -p "${SCRIPT_DIR}/${SCRIPT_NAME}.asp" /www/user/"$am_webui_page" if [ ! -f /tmp/menuTree.js ]; then cp /www/require/modules/menuTree.js /tmp/ mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js fi if ! /bin/grep -q "{url: \"$am_webui_page\", tabName: \"${SCRIPT_NAME_FANCY}\"}," /tmp/menuTree.js; then umount /www/require/modules/menuTree.js 2>/dev/null sed -i "\~tabName: \"${SCRIPT_NAME_FANCY}\"},~d" /tmp/menuTree.js sed -i "/url: \"QoS_Stats.asp\", tabName:/i {url: \"$am_webui_page\", tabName: \"${SCRIPT_NAME_FANCY}\"}," /tmp/menuTree.js mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js fi fi /usr/bin/flock -u "$FD" [ ! -d "/www/ext/${SCRIPT_NAME}" ] && mkdir -p "/www/ext/${SCRIPT_NAME}" } Init_UserScript() { # Properly setup an empty Merlin user script if [ -z "$1" ]; then return fi userscript="/jffs/scripts/$1" if [ ! -f "$userscript" ]; then # If script doesn't exist yet, create with shebang printf "#!/bin/sh\n\n" > "$userscript" elif [ -f "$userscript" ] && ! head -1 "$userscript" | /bin/grep -qE "^#!/bin/sh"; then # Script exists but no shebang, so insert it at line 1 sed -i '1s~^~#!/bin/sh\n~' "$userscript" elif [ "$(tail -c1 "$userscript" | wc -l)" = "0" ]; then # Script exists with shebang, but no linefeed before EOF; makes appending content unpredictable if missing printf "\n" >> "$userscript" fi if [ ! -x "$userscript" ]; then # Ensure script is executable by owner chmod 755 "$userscript" fi unset userscript } # Init_UserScript Cake_Install(){ if ! nvram get rc_support | /bin/grep -q "cake"; then if nvram get buildno | /bin/grep -q "^386"; then Print_Output "false" "This version of the script is not compatible with your router firmware version. Installing legacy version 1.0.8!" "$WARN" curl -fsL --retry 3 --connect-timeout 3 "https://raw.githubusercontent.com/ttgapers/cakeqos-merlin/386/cake-qos.sh" -o "$0" && exec sh "$0" install exit 1 else Print_Output "false" "This version of the script is not compatible with your router firmware version. Installing legacy version 1.0.7!" "$WARN" curl -fsL --retry 3 --connect-timeout 3 "https://raw.githubusercontent.com/ttgapers/cakeqos-merlin/384/cake-qos.sh" -o "$0" && exec sh "$0" install exit 1 fi fi if [ "$(nvram get qos_enable)" != "1" ] || [ "$(nvram get qos_type)" != "9" ]; then Print_Output "true" "Enable Cake QoS scheduler in the firmware..." "$PASS" tc qdisc del dev eth0 root 2>/dev/null tc qdisc del dev br0 root 2>/dev/null nvram set qos_enable=1 nvram set qos_type=9 nvram set fc_disable=0 nvram set runner_disable=0 if [ "$RMODEL" = "RT-AX58U" ] || [ "$RMODEL" = "RT-AX3000" ]; then fc config --hw-accel 0 else runner disable 2>/dev/null fi fc disable 2>/dev/null fc flush 2>/dev/null if [ -z "$(nvram get qos_ibw)" ]; then Print_Output "true" "Download bandwidth not set, setting to Automatic..." "$PASS" nvram set qos_ibw=0; fi if [ -z "$(nvram get qos_obw)" ]; then Print_Output "true" "Upload bandwidth not set, setting to Automatic..." "$PASS" nvram set qos_obw=0; fi nvram commit fi if [ "$(nvram get jffs2_scripts)" != "1" ]; then nvram set jffs2_scripts=1 nvram commit Print_Output "true" "Custom JFFS scripts enabled" "$WARN" fi # Remove old Cake packages if [ -f /opt/lib/modules/sch_cake.ko ]; then Print_Output "true" "Removing old CakeQOS-Merlin 1.0 packages and modifications" "$WARN" oldiface="$(nvram get wan0_ifname)" cru d "$SCRIPT_NAME_FANCY" sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/nat-start /jffs/scripts/services-stop /opt/sbin/tc qdisc del dev ${oldiface} ingress 2>/dev/null /opt/sbin/tc qdisc del dev ifb9${oldiface} root 2>/dev/null /opt/sbin/tc qdisc del dev ${oldiface} root 2>/dev/null ip link del ifb9${oldiface} 2>/dev/null rmmod sch_cake 2>/dev/null opkg --autoremove remove sched-cake-oot opkg --autoremove remove tc-adv rm "${SCRIPT_DIR}/${SCRIPT_NAME}.cfg" 2>/dev/null fi Print_Output "false" "Installing CakeQOS-Merlin $version..." "$PASS" # Add to service-event Init_UserScript "service-event" sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/service-event echo "[ \"\$2\" = \"qos\" ] && ${SCRIPT_DIR}/${SCRIPT_NAME} config # $SCRIPT_NAME_FANCY" >> /jffs/scripts/service-event # Add to service-event-end Init_UserScript "service-event-end" sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/service-event-end echo "if echo \"\$2\" | /bin/grep -q \"^cakeqos\"; then { sh ${SCRIPT_DIR}/${SCRIPT_NAME} \"\${2#cakeqos}\" & } ; fi # $SCRIPT_NAME_FANCY" >> /jffs/scripts/service-event-end echo "[ \"\$2\" = \"qos\" ] && ${SCRIPT_DIR}/${SCRIPT_NAME} statsupdate # $SCRIPT_NAME_FANCY" >> /jffs/scripts/service-event-end # Add to services-start Init_UserScript "services-start" sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/services-start echo "sh ${SCRIPT_DIR}/${SCRIPT_NAME} mountui # $SCRIPT_NAME_FANCY" >> /jffs/scripts/services-start # Add to firewall-start Init_UserScript "firewall-start" sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/firewall-start echo "sh ${SCRIPT_DIR}/${SCRIPT_NAME} startup # $SCRIPT_NAME_FANCY" >> /jffs/scripts/firewall-start if [ -d "/opt/bin" ] && { [ ! -L "/opt/bin/${SCRIPT_NAME}" ] || [ "$(readlink /opt/bin/${SCRIPT_NAME})" != "${SCRIPT_DIR}/${SCRIPT_NAME}" ]; } then rm -rf /opt/bin/${SCRIPT_NAME} ln -s "${SCRIPT_DIR}/${SCRIPT_NAME}" "/opt/bin/${SCRIPT_NAME}" fi Download_File "${SCRIPT_NAME}.asp" "${SCRIPT_DIR}/${SCRIPT_NAME}.asp" Cake_Mount_UI Print_Output "false" "Customize Cake in the WebUI under Adaptive QoS / $SCRIPT_NAME_FANCY" "$PASS" } Cake_Uninstall(){ printf "Removing WebUI...\n" prev_webui_page="$(sed -nE "s/^\{url\: \"(user[0-9]+\.asp)\"\, tabName\: \"${SCRIPT_NAME_FANCY}\"\}\,$/\1/p" /tmp/menuTree.js 2>/dev/null)" if [ -n "$prev_webui_page" ]; then # Remove page from the UI menu system LOCKFILE=/tmp/addonwebui.lock FD=386 eval exec "$FD>$LOCKFILE" /usr/bin/flock -x "$FD" umount /www/require/modules/menuTree.js 2>/dev/null sed -i "\~tabName: \"${SCRIPT_NAME_FANCY}\"},~d" /tmp/menuTree.js if diff -q /tmp/menuTree.js /www/require/modules/menuTree.js >/dev/null 2>&1; then # no more custom pages mounted, so remove the file rm /tmp/menuTree.js else # Still some modifications from another script so remount mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js fi /usr/bin/flock -u "$FD" # Remove last mounted asp page rm -f /www/user/"$prev_webui_page" 2>/dev/null fi sed -i '\~# CakeQOS-Merlin~d' /jffs/scripts/service-event /jffs/scripts/service-event-end /jffs/scripts/services-start /jffs/scripts/firewall-start #/jffs/scripts/qos-start sed -i "/^cakeqos_/d" /jffs/addons/custom_settings.txt rm -rf "/opt/bin/${SCRIPT_NAME}" "${SCRIPT_DIR}" "/www/ext/${SCRIPT_NAME}" /jffs/configs/cake-qos.conf.add 2>/dev/null } compare_remote_version() { # Check version on Github and determine the difference with the installed version # Outcomes: Version update, Hotfix (w/o version change), or no update # Fetch version of the shell script on Github remotever="$(curl -fsN --retry 3 --connect-timeout 3 "${SCRIPT_REMOTEDIR}/${SCRIPT_NAME}.sh" | /bin/grep "^version=" | sed -e 's/version=//')" if [ "$(echo $version | sed 's/[^0-9]*//g')" -lt "$(echo $remotever | sed 's/[^0-9]*//g')" ]; then # strip the . from version string for numeric comparison # version upgrade echo "$remotever" else # If no version change, calculate md5sum of local and remote files # to determine if a hotfix has been published localmd5="$(md5sum "$0" | awk '{print $1}')" remotemd5="$(curl -fsL --retry 3 --connect-timeout 3 "${SCRIPT_REMOTEDIR}/${SCRIPT_NAME}.sh" | md5sum | awk '{print $1}')" localmd5asp="$(md5sum "${SCRIPT_DIR}/${SCRIPT_NAME}.asp" | awk '{print $1}')" remotemd5asp="$(curl -fsL --retry 3 --connect-timeout 3 "${SCRIPT_REMOTEDIR}/${SCRIPT_NAME}.asp" | md5sum | awk '{print $1}')" if [ "$localmd5" != "$remotemd5" ] || [ "$localmd5asp" != "$remotemd5asp" ]; then # hotfix printf "Hotfix\n" else printf "NoUpdate\n" fi fi } # compare_remote_version Cake_Update(){ # Check for, and optionally apply updates. # Parameter options: check (do not update), silent (update without prompting) printf "Checking for updates\n" # Update the webui status thorugh detect_update.js ajax call. printf "var verUpdateStatus = \"%s\";\n" "InProgress" > /www/ext/${SCRIPT_NAME}/detect_update.js updatestatus="$(compare_remote_version)" # Check to make sure we got back a valid status from compare_remote_version(). If not, indicate Error. case "$updatestatus" in 'NoUpdate'|'Hotfix'|[0-9].[0-9].[0-9]) ;; *) updatestatus="Error" esac printf "var verUpdateStatus = \"%s\";\n" "$updatestatus" > /www/ext/${SCRIPT_NAME}/detect_update.js if [ "$1" = "check" ]; then # Do not proceed with any updating if check function requested return fi if [ "$mode" = "interactive" ] && [ -z "$1" ]; then case "$updatestatus" in 'NoUpdate') printf " You have the latest version installed\n" printf " Would you like to overwrite your existing installation anyway? [1=Yes 2=No]: " ;; 'Hotfix') printf " %s hotfix is available.\n" "$SCRIPT_NAME_FANCY" printf " Would you like to update now? [1=Yes 2=No]: " ;; 'Error') printf " Error determining remote version status!\n" return ;; *) # New Version Number printf " %s v%s is now available!\n" "$SCRIPT_NAME_FANCY" "$updatestatus" printf " Would you like to update now? [1=Yes 2=No]: " ;; esac read -r yn printf "\n" if [ "$yn" != "1" ]; then printf " No Changes have been made\n" return 0 fi fi printf "Installing: %s...\n\n" "$SCRIPT_NAME_FANCY" Download_File "${SCRIPT_NAME}.sh" "$0" Download_File "${SCRIPT_NAME}.asp" "${SCRIPT_DIR}/${SCRIPT_NAME}.asp" exec sh "$0" install exit } Display_Line(){ printf '\n#########################################################\n\n' } Is_Valid_CIDR() { /bin/grep -qE '^[!]?([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$' } # Is_Valid_CIDR Is_Valid_Port() { /bin/grep -qE '^[!]?([0-9]{1,5})((:[0-9]{1,5})?|(,[0-9]{1,5})*)$' } # Is_Valid_Port get_tin_dscp() { case "${1}" in 0) printf "%s\n" "CS1" ;; # Bulk 1) printf "%s\n" "AF31" ;; # Streaming 2) printf "%s\n" "EF" ;; # Voice 3) printf "%s\n" "AF41" ;; # Conferencing 4) printf "%s\n" "CS4" ;; # Gaming *) printf "%s\n" "CS0" ;; # Other esac } apply_iptablesrule() { # Process an iptables custom rule into the appropriate iptables syntax # Input: $1 = local IP (e.g. 192.168.1.100 !192.168.1.100 192.168.1.100/31 !192.168.1.100/31) # $2 = remote IP (e.g. 9.9.9.9 !9.9.9.9 9.9.9.0/24 !9.9.9.0/24) # $3 = protocol (e.g. both, tcp, or udp) # $4 = local port (e.g. 443 !443 1234:5678 !1234:5678 53,123,853 !53,123,853) # $5 = remote port (e.g. 443 !443 1234:5678 !1234:5678 53,123,853 !53,123,853) # $6 = CAKE tin (e.g. 0-7) tmp_OLDIFS="$IFS" IFS="$OLDIFS" # local IP # Check for acceptable IP format if echo "${1}" | Is_Valid_CIDR; then # print ! (if present) and remaining CIDR UP_Lip="$(echo "${1}" | sed -E 's/^([!])?/\1 -s /')" else UP_Lip="" fi # remote IP # Check for acceptable IP format if echo "${2}" | Is_Valid_CIDR; then # print ! (if present) and remaining CIDR UP_Rip="$(echo "${2}" | sed -E 's/^([!])?/\1 -d /')" else UP_Rip="" fi # protocol (required when port specified) if [ "${3}" = "tcp" ] || [ "${3}" = "udp" ]; then # print protocol directly PROTOS="${3}" elif [ "${#4}" -gt "1" ] || [ "${#5}" -gt "1" ]; then # proto=both & ports are defined PROTOS="tcp udp" # separated by > because IFS will be temporarily set to '>' by calling function. TODO Fix Me else # neither proto nor ports defined PROTOS="all" fi # local port if echo "${4}" | Is_Valid_Port; then # Use multiport to specify any port specification: # single port, multiple ports, port range UP_Lport="-m multiport $(echo "${4}" | sed -E 's/^([!])?/\1 --sports /')" else UP_Lport="" fi # remote port if echo "${5}" | Is_Valid_Port; then # Use multiport to specify any port specification: # single port, multiple ports, port range UP_Rport="-m multiport $(echo "${5}" | sed -E 's/^([!])?/\1 --dports /')" else UP_Rport="" fi # if all parameters are empty stop processing the rule if [ -z "${UP_Lip}${UP_Rip}${UP_Lport}${UP_Rport}" ]; then return fi # destination tin # numbers come from webui select options for Tin field Dst_tin="$(get_tin_dscp "${6}")" if [ -z "${Dst_tin}" ]; then return fi UP_dst="-j DSCP --set-dscp-class ${Dst_tin}" # This block is redirected to the /tmp/cake-qos_iprules file, so no extraneous output, please # If proto=both we have to create 2 statements, one for tcp and one for udp. for proto in ${PROTOS}; do # upload ipv4 iptables -t mangle -A "${SCRIPT_NAME_FANCY}" -o "${iface}" ${UP_Lip} ${UP_Rip} -p ${proto} ${UP_Lport} ${UP_Rport} ${UP_dst} # If rule contains no IPv4 local or remote addresses, and IPv6 is enabled, add a corresponding rule for IPv6 if [ "${IPv6_enabled}" != "disabled" ] && [ -z "${UP_Lip}" ] && [ -z "${UP_Rip}" ]; then # upload ipv6 ip6tables -t mangle -A "${SCRIPT_NAME_FANCY}" -o "${iface}" -p ${proto} ${UP_Lport} ${UP_Rport} ${UP_dst} fi done IFS="$tmp_OLDIFS" } # apply_iptablesrule startup() { if [ "$(nvram get qos_enable)" != "1" ] || [ "$(nvram get qos_type)" != "9" ]; then Print_Output "true" "Cake QoS is not enabled. Skipping ${SCRIPT_NAME_FANCY} startup." return 1 fi # Cake qos not enabled Check_Lock # Read settings from Addon API config file. if [ "$(am_settings_get cakeqos_ulrules)" = "1" ]; then iptables_rules="$(am_settings_get cakeqos_iptables)" # Only apply iptables rules if user defined any if [ -n "${iptables_rules}" ]; then Print_Output "true" "Applying iptables rules" iptables -t mangle -N "${SCRIPT_NAME_FANCY}" 2>/dev/null if ! iptables -t mangle -C POSTROUTING -j "${SCRIPT_NAME_FANCY}" 2>/dev/null; then iptables -t mangle -A POSTROUTING -j "${SCRIPT_NAME_FANCY}" fi if [ "${IPv6_enabled}" != "disabled" ]; then ip6tables -t mangle -N "${SCRIPT_NAME_FANCY}" 2>/dev/null if ! ip6tables -t mangle -C POSTROUTING -j "${SCRIPT_NAME_FANCY}" 2>/dev/null; then ip6tables -t mangle -A POSTROUTING -j "${SCRIPT_NAME_FANCY}" fi fi iptables -t mangle -F "${SCRIPT_NAME_FANCY}" 2>/dev/null if [ "${IPv6_enabled}" != "disabled" ]; then ip6tables -t mangle -F "${SCRIPT_NAME_FANCY}" 2>/dev/null fi # loop through iptables rules and write an iptables command to a temporary file for later execution OLDIFS="${IFS}" # Save existing field separator IFS=">" # Set custom field separator to match rule format # read the rules, 1 per line and break into separate fields echo "${iptables_rules}" | sed 's//dev/null 2>&1 fi fi } # startup Kill_Lock() { if [ -f "/tmp/${SCRIPT_NAME}.lock" ] && [ -d "/proc/$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock")" ]; then logmsg "[*] Killing Running Process (pid=$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock"))" logmsg "[*] $(ps | awk -v pid="$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock")" '$1 == pid')" kill "$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock")" fi rm -rf "/tmp/${SCRIPT_NAME}.lock" } # Kill_Lock Check_Lock() { if [ -f "/tmp/${SCRIPT_NAME}.lock" ] && [ -d "/proc/$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock")" ] && [ "$(sed -n '1p' "/tmp/${SCRIPT_NAME}.lock")" != "$$" ]; then Kill_Lock fi printf "%s\n" "$$" > "/tmp/${SCRIPT_NAME}.lock" lock="true" } # Check_Lock Cake_Menu(){ reloadmenu="1" echo "Select an option" echo "[1] --> Check cake status" echo "[2] --> Update $SCRIPT_NAME_FANCY" echo "[3] --> Install $SCRIPT_NAME_FANCY" echo "[4] --> Uninstall $SCRIPT_NAME_FANCY" echo "[5] --> Debug info" echo echo "[e] --> Exit" echo Display_Line while true; do echo printf "[1-5]: " read -r "menu1" echo case "$menu1" in 1) option1="status" while true; do echo "Select Status Option:" echo "[1] --> Download Status" echo "[2] --> Upload Status" echo "[3] --> General Status" echo echo "[e] --> Exit" echo printf "[1-3]: " read -r "menu2" echo case "$menu2" in 1) option2="download" break ;; 2) option2="upload" break ;; 3) option2="general" break ;; e|exit|back|menu) unset "option1" "option2" clear Cake_Menu break ;; esac done break ;; 2) option1="updatecheck" break ;; 3) option1="install" break ;; 4) option1="uninstall" break ;; 5) option1="debug" break ;; e) echo "Exiting!" echo exit 0 ;; *) echo "$menu1 Isn't An Option!" echo ;; esac done } if [ -z "$1" ]; then Cake_Menu fi if [ -n "$option1" ]; then set "$option1" "$option2" echo "[$] $0 $*" | tr -s " " fi arg1="$1" Display_Line iface="$(get_wanif)" case "$arg1" in config) Cake_Write_QOS ;; startup) Print_Output "true" "$0 (pid=$$) called in ${mode} mode with $# args: $*" startup ;; mountui) Cake_Mount_UI ;; status) if Cake_CheckStatus; then case "$2" in download) tc -s qdisc show dev ifb4${iface} ;; upload) tc -s qdisc show dev ${iface} root ;; general) Print_Output "false" "> Download Status:" "$PASS" echo "$STATUS_DOWNLOAD" echo Print_Output "false" "> Upload Status:" "$PASS" echo "$STATUS_UPLOAD" ;; esac else Print_Output "false" "Not running..." "$WARN" fi ;; update*) # updatecheck, updatesilent, or plain update Cake_Update "${arg1#update}" # strip 'update' from arg1 to pass to update function ;; install|start) # start is used to upgrade v1.0.x users from nat-start invocation Cake_Install printf "Restarting QoS..." service "restart_qos;restart_firewall" ;; uninstall) Cake_Uninstall service "restart_qos;restart_firewall" echo exit 0 ;; debug) printf "Prio: %s %s\n" "$(Cake_Get_Prio 'dl')" "$(Cake_Get_Prio 'ul')" printf "Flow Iso: %s %s\n" "$(Cake_Get_FlowIso 'dl')" "$(Cake_Get_FlowIso 'ul')" printf "NAT: %s %s\n" "$(Cake_Get_NAT 'dl')" "$(Cake_Get_NAT 'ul')" printf "Wash: %s %s\n" "$(Cake_Get_Wash 'dl')" "$(Cake_Get_Wash 'ul')" printf "ACK: %s %s\n" "$(Cake_Get_ACK 'dl')" "$(Cake_Get_ACK 'ul')" printf "Custom: %s %s\n" "$(Cake_Get_CustomOpts 'dl')" "$(Cake_Get_CustomOpts 'ul')" ;; statsupdate) Cake_GetStatus ;; *) Print_Output "false" "Usage;" "$WARN" printf '\n%-32s | %-55s\n' "cake-qos status download" "check the current download status of $SCRIPT_NAME" printf '%-32s | %-55s\n' "cake-qos status upload" "check the current upload status of $SCRIPT_NAME" printf '%-32s | %-55s\n\n' "cake-qos status general" "check the current general status of $SCRIPT_NAME" printf '%-32s | %-55s\n' "cake-qos install" "install and configure $SCRIPT_NAME" printf '%-32s | %-55s\n' "cake-qos uninstall" "uninstall and remove all traces of $SCRIPT_NAME" printf '%-32s | %-55s\n' "cake-qos update" "check for updates of $SCRIPT_NAME" printf '%-32s | %-55s\n' "cake-qos debug" "show debug info from $SCRIPT_NAME" ;; esac Display_Line if [ -n "$reloadmenu" ]; then echo; printf "[*] Press Enter To Continue..."; read -r "reloadmenu"; exec "$0"; fi if [ "${lock}" = "true" ]; then rm -rf "/tmp/${SCRIPT_NAME}.lock"; fi