Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,14 @@ qosStr.DBSect="QoS Download Bandwidth Distribution";
qosStr.uTFrm="Upload Time Frame";
qosStr.dTFrm="Download Time Frame";

//known device groups
qosStr.DevGroup="Device Group";

qosStr.CakeMode="Use Simple Mode (CAKE) instead of Advanced (HFSC)";
qosStr.CakeAbout="Simple mode uses CAKE, a modern queuing algorithm that automatically prevents bufferbloat and fairly shares bandwidth between all devices and flows. No manual class configuration is needed -- just set your bandwidth and enable. CAKE is recommended for most users.";
qosStr.CakeOverhead="Link overhead compensation";
qosStr.CakeOHNone="None (direct Ethernet)";
qosStr.CakeTins="CAKE automatically prioritises traffic into four tiers: Voice (VoIP, gaming), Video (streaming), Best Effort (web), and Bulk (downloads, torrents). Apps that mark their own traffic are classified automatically.";
qosStr.CakeStatus="CAKE status";
qosStr.CakeActive="Active";
qosStr.CakeInactive="Inactive (restart QoS to apply)";
170 changes: 161 additions & 9 deletions package/plugin-gargoyle-qos/files/www/js/qos.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ function saveChanges()
var bwmonCleanCommand = bwmonCleanCommand + "if [ -d /tmp/data/bwmon/ ] ; then rm /tmp/data/bwmon/qos-" + direction + "-* >/dev/null 2>&1 ; fi ;\n";


var isCake = document.getElementById("cake_enabled").checked;
uci.set("qos_gargoyle", direction, "cake_enabled", isCake ? "1" : "0");
if(isCake)
{
var overhead = document.getElementById("cake_overhead").value;
if(overhead != "none")
{
uci.set("qos_gargoyle", direction, "cake_overhead", overhead);
}
else
{
uci.remove("qos_gargoyle", direction, "cake_overhead");
}
}

//Save the setting of the qos_monenable flag
if (direction == "download")
{
Expand Down Expand Up @@ -54,7 +69,7 @@ function saveChanges()
var switchingFullQosEnabled = (fullQosWillBeEnabled != qosEnabled);

//Is the user requesting disable of this direction of QoS?
if(disabled)
if(disabled && !isCake)
{
//If this page was enabled before and the other was not then stop and disable QoS
if(qosEnabled && alternateBandwidth == "" )
Expand Down Expand Up @@ -83,6 +98,21 @@ function saveChanges()
{
errors=qosStr.TotErr;
}
else if(isCake)
{
qosEnabled = true;
uci.set("gargoyle", "status", "qos", "300");
uci.set("qos_gargoyle", direction, "total_bandwidth", document.getElementById("total_bandwidth").value);
if(switchingFullQosEnabled && qosQuotasExist())
{
commands = uci.getScriptCommands(uciOriginal) + "\n/etc/init.d/qos_gargoyle enable ;\nsh /usr/lib/gargoyle/restart_firewall.sh ;\n";
}
else
{
commands = "\n/etc/init.d/qos_gargoyle start ;\n/etc/init.d/qos_gargoyle enable ;\n";
commands = uci.getScriptCommands(uciOriginal) + "\n" + commands + stopbwmon + bwmonCleanCommand + startbwmon;
}
}
else
{
qosEnabled = true;
Expand Down Expand Up @@ -560,6 +590,10 @@ function resetData()

}

document.getElementById("cake_enabled").checked = cakeEnabled;
document.getElementById("cake_overhead").value = cakeOverhead;
setCakeMode();

setQosEnabled();

//Startup the dynamic screen updates.
Expand Down Expand Up @@ -642,6 +676,29 @@ function setQosEnabled()
resetServiceClassControls();
}

function setCakeMode()
{
var isCake = document.getElementById("cake_enabled").checked;
var hfscPanels = ["qos_class_table_container", "qos_rule_table_container",
"add_class_container", "add_rule_container",
"default_class_label", "default_class",
"qos_monitor_container", "acc_panel"];
for(var i = 0; i < hfscPanels.length; i++)
{
var el = document.getElementById(hfscPanels[i]);
if(el) { el.style.display = isCake ? "none" : ""; }
}
var cakePanel = document.getElementById("cake_options_panel");
if(cakePanel) { cakePanel.style.display = isCake ? "" : "none"; }
var cakeStatusPanel = document.getElementById("cake_status_panel");
if(cakeStatusPanel) { cakeStatusPanel.style.display = isCake ? "" : "none"; }
// Bandwidth field must always be editable in CAKE mode (used for shaper bandwidth)
if(isCake)
{
setElementEnabled(document.getElementById("total_bandwidth"), true, "");
}
}

function addClassificationRule()
{
errors = proofreadClassificationRule();
Expand All @@ -651,6 +708,7 @@ function addClassificationRule()
}
else
{
syncGroupIpFields();

addRuleMatchControls = ["ip_family", "source_ip", "source_port", "dest_ip", "dest_port", "max_pktsize", "min_pktsize", "transport_protocol", "connbytes_kb", "app_protocol"];
displayList = [UI.IPFam+": $", qosStr.Src+": $", qosStr.SrcP+": $", qosStr.Dst+": $", qosStr.DstP+": $", qosStr.MaxPktLen+": $ "+UI.byt, qosStr.MinPktLen+": $ "+UI.byt, qosStr.TrProto+": $", qosStr.Connb+": $ "+UI.KBy, qosStr.APro+": $"];
Expand Down Expand Up @@ -695,6 +753,7 @@ function proofreadClassificationRule()
alwaysValid = function(text){return 0;};
validateQOSIPRangeAndFamily = function(ipstr)
{
if(ipstr.indexOf("GROUP:") === 0) { return 0; }
var retVal = 0;
var ipfam = getIPFamily(ipstr);
var selfam = document.getElementById("ip_family").value;
Expand Down Expand Up @@ -748,6 +807,16 @@ function resetRuleControls()
checkbox.checked =false;
enableAssociatedField( checkbox, ruleControlIds[ruleControlIndex], "");
}

["source", "dest"].forEach(function(w)
{
var typeEl = document.getElementById(w + "_ip_type");
typeEl.value = "ip";
typeEl.disabled = true;
document.getElementById(w + "_group_select").disabled = true;
document.getElementById(w + "_ip_text_container").style.display = "";
document.getElementById(w + "_group_container").style.display = "none";
});
}

function addServiceClass()
Expand Down Expand Up @@ -906,6 +975,7 @@ function editRuleTableRow(editRuleWindowRow)
}
else
{
syncGroupIpFields();
addRuleMatchControls = ["ip_family", "source_ip", "source_port", "dest_ip", "dest_port", "max_pktsize", "min_pktsize", "transport_protocol", "connbytes_kb", "app_protocol"];
displayList = [UI.IPFam+": $", qosStr.Src+": $", qosStr.SrcP+": $", qosStr.Dst+": $", qosStr.DstP+": $", qosStr.MaxPktLen+": $ "+UI.byt, qosStr.MinPktLen+": $ "+UI.byt, qosStr.TrProto+": $", qosStr.Connb+": $ "+UI.KBy, qosStr.APro+": $"];
ruleText = ""
Expand Down Expand Up @@ -1098,19 +1168,17 @@ function updatetc()
{
updateInProgress = true;

var commands="tc -s class show dev ";
var cakeActive = document.getElementById("cake_enabled") && document.getElementById("cake_enabled").checked;
var tcDev = direction == "download" ? "ifb0" : currentWanName;
var commands;

if (direction == "download")
if(cakeActive)
{
commands = commands + "ifb0";
commands = "tc qdisc show dev " + tcDev;
}
else
{
/*
* NOTE: This NEEDS to be "currentWanName" variable NOT "currentWanIf" Variable!!!
* If this doesn't work the problem is in gargoyle_header_footer utility, not here
*/
commands = commands + currentWanName;
commands = "tc -s class show dev " + tcDev;
}

var param = getParameterDefinition("commands", commands) + "&" + getParameterDefinition("hash", document.cookie.replace(/^.*hash=/,"").replace(/[\t ;]+.*$/, ""));
Expand All @@ -1119,6 +1187,24 @@ function updatetc()
{
if(req.readyState == 4)
{
if(cakeActive)
{
var statusEl = document.getElementById("cake_active_status");
if(statusEl)
{
if(req.responseText.indexOf("cake") >= 0)
{
statusEl.innerHTML = "<span style='color:green;font-weight:bold;'>Active</span>";
}
else
{
statusEl.innerHTML = "<span style='color:red;'>Inactive &mdash; restart QoS to apply</span>";
}
}
updateInProgress = false;
return;
}

/*Only match leaf classes and class with 2 digit class numbers since on the upload side class 1:127 is only for qosmon*/
var lines = req.responseText.match(/hfsc\s1:[0-9]{1,2}\s.+leaf.+\n.+Sent\s[0-9]+/g);
var d=new Date();
Expand Down Expand Up @@ -1240,8 +1326,54 @@ function resetFairLinkLimit()
runAjax("POST", "utility/run_commands.sh", param, stateChangeFunction);
}

function populateQosGroupSelects()
{
["source_group_select", "dest_group_select"].forEach(function(selId)
{
var sel = document.getElementById(selId);
if(!sel) { return; }
while(sel.firstChild) { sel.removeChild(sel.firstChild); }
var gi;
for(gi = 0; gi < knownDeviceGroups.length; gi++)
{
var opt = document.createElement("option");
opt.value = knownDeviceGroups[gi];
opt.textContent = knownDeviceGroups[gi];
sel.appendChild(opt);
}
});
}

function syncGroupIpFields()
{
["source", "dest"].forEach(function(w)
{
var typeEl = document.getElementById(w + "_ip_type");
if(!typeEl.disabled && typeEl.value == "group")
{
var sel = document.getElementById(w + "_group_select");
document.getElementById(w + "_ip").value = sel.options.length > 0 ? "GROUP:" + sel.value : "";
}
});
}

function updateRuleIpMode(which)
{
var checkEl = document.getElementById("use_" + which + "_ip");
var enabled = checkEl.checked && !checkEl.disabled;
var typeEl = document.getElementById(which + "_ip_type");
typeEl.disabled = !enabled;
var mode = typeEl.value;
var ipField = document.getElementById(which + "_ip");
ipField.disabled = !(enabled && mode == "ip");
document.getElementById(which + "_group_select").disabled = !(enabled && mode == "group");
document.getElementById(which + "_ip_text_container").style.display = (enabled && mode == "ip") ? "" : "none";
document.getElementById(which + "_group_container").style.display = (enabled && mode == "group") ? "" : "none";
}

function addRuleModal()
{
populateQosGroupSelects();
modalButtons = [
{"title" : UI.Add, "classes" : "btn btn-primary", "function" : addClassificationRule},
"defaultDismiss"
Expand All @@ -1256,6 +1388,7 @@ function addRuleModal()

function editRuleModal(triggerEl)
{
populateQosGroupSelects();
editRow=triggerEl.parentNode.parentNode;
modalButtons = [
{"title" : UI.CApplyChanges, "classes" : "btn btn-primary", "function" : function(){editRuleTableRow(editRow);}},
Expand Down Expand Up @@ -1312,6 +1445,23 @@ function editRuleModal(triggerEl)
enableAssociatedField(ruleControlCheckbox, ruleControlId, "", document);
}
}

// Restore group mode for source/dest if criteria contains GROUP: prefix
["source", "dest"].forEach(function(w)
{
var ipField = document.getElementById(w + "_ip");
if(ipField.value.indexOf("GROUP:") === 0)
{
var groupName = ipField.value.substring(6);
ipField.value = "";
ipField.disabled = true;
var typeEl = document.getElementById(w + "_ip_type");
typeEl.value = "group";
setSelectedValue(w + "_group_select", groupName, document);
updateRuleIpMode(w);
}
});

setSelectedText("classification", editRow.childNodes[1].firstChild.data, document);
updateIPControls(document.getElementById("ip_family"));

Expand Down Expand Up @@ -1418,6 +1568,8 @@ function updateIPControls(triggerEl)
}
enableAssociatedField(checkEl,elArr[x], '');
}
updateRuleIpMode('source');
updateRuleIpMode('dest');
}

function proofreadQOSIPRange(triggerEl)
Expand Down
38 changes: 37 additions & 1 deletion package/plugin-gargoyle-qos/files/www/qos_download.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
else
echo "var qosEnabled = false;"
fi

echo "var knownDeviceGroups = [];"
uci show dhcp 2>/dev/null | grep '^dhcp\.[^.]*\.group=' | sed "s/^[^=]*=//; s/'//g" | sort -u | grep -v '^$' | while read grp ; do
escaped=$(printf '%s' "$grp" | sed 's/\\/\\\\/g; s/"/\\"/g')
printf 'knownDeviceGroups.push("%s");\n' "$escaped"
done

cake_enabled=$(uci get qos_gargoyle.download.cake_enabled 2>/dev/null)
echo "var cakeEnabled = $([ "$cake_enabled" = "1" ] && echo "true" || echo "false");"
cake_overhead=$(uci get qos_gargoyle.download.cake_overhead 2>/dev/null)
echo "var cakeOverhead = \"${cake_overhead:-none}\";"
cake_active=$(tc qdisc show dev ifb0 2>/dev/null | grep -c "^qdisc cake" || echo 0)
%>

//-->
Expand Down Expand Up @@ -49,6 +61,30 @@

<div class="internal_divider"></div>

<div class="row form-group" id="cake_mode_container">
<span class="col-xs-12">
<input type="checkbox" id="cake_enabled" onclick="setCakeMode()" />
<label for="cake_enabled"><%~ CakeMode %></label>
</span>
</div>

<div id="cake_options_panel" class="row form-group">
<label class="col-xs-5" for="cake_overhead"><%~ CakeOverhead %>:</label>
<span class="col-xs-7">
<select class="form-control" id="cake_overhead">
<option value="none"><%~ CakeOHNone %></option>
<option value="ethernet">Ethernet</option>
<option value="pppoe">PPPoE</option>
<option value="vdsl">VDSL/PTM</option>
</select>
</span>
</div>

<div id="cake_status_panel" class="row form-group" <% [ "$cake_enabled" != "1" ] && printf 'style="display:none;"' %>>
<label class="col-xs-5">CAKE status:</label>
<span class="col-xs-7" id="cake_active_status"><% if [ "${cake_active:-0}" -gt 0 ] 2>/dev/null; then printf '<span style="color:green;font-weight:bold;">Active</span>'; else printf '<span style="color:orange;">Configured &mdash; restart QoS to activate</span>'; fi %></span>
</div>

<div id="qos_rule_table_container" class="bottom_gap table-responsive"></div>

<div class="row form-group">
Expand Down Expand Up @@ -126,7 +162,7 @@
</div>
</div>

<div class="col-lg-6">
<div class="col-lg-6" id="acc_panel">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%~ DACCSect %></h3>
Expand Down
Loading