mirror of
https://github.com/pfsense/pfsense.git
synced 2025-10-26 11:38:35 +00:00
Merge pull request #3941 from mattund/master
This commit is contained in:
commit
88b10e56c1
@ -31,6 +31,200 @@ require_once("util.inc");
|
||||
require_once("notices.inc");
|
||||
include_once("interfaces.inc");//required for convert_real_interface_to_friendly_interface_name() in get_queue_stats() to get altq interface name.
|
||||
|
||||
/* Limiter patch */
|
||||
// I need this crazy looking model/struct to specify various algos, ecn caps, and params.
|
||||
// update as needed when dummynet changes
|
||||
|
||||
// List of schedulers ('type' command on scheduler/pipe) that dummynet/ipfw is supposed to support
|
||||
function getSchedulers() {
|
||||
return array(
|
||||
"fifo" => array(
|
||||
"name" => "FIFO",
|
||||
"parameter_format" => "type fifo",
|
||||
"description" => "First-in-First-out is a fundamental queueing discipline which ensures packet sequence.",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"qfq" => array(
|
||||
"name" => "Quick Fair Queueing",
|
||||
"parameter_format" => "type qfq",
|
||||
"description" => "QFQ is a fast, low-complexity Approximated Fair Queueing scheduler.",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"rr" => array(
|
||||
"name" => "Round Robin",
|
||||
"parameter_format" => "type rr",
|
||||
"description" => "Round Robin (RR) schedules packets in a round-robin fashion.",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"wf2q+" => array(
|
||||
"name" => "Worst-case Weighted fair Queueing",
|
||||
"parameter_format" => "type wf2q+",
|
||||
"description" => "Worst-case Weighted fair Queueing (WF2Q+) schedules flows with an associated weight.",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"prio" => array(
|
||||
"name" => "PRIO",
|
||||
"parameter_format" => "type prio",
|
||||
"description" => "PRIO schedules packets by their corresponding priority.",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"fq_codel" => array(
|
||||
"name" => "FQ_CODEL",
|
||||
"parameter_format" => "type fq_codel target %sms interval %sms quantum %s limit %s flows %s",
|
||||
"description" => "CoDel is a novel \"no knobs\", \"just works\", \"handles variable bandwidth and RTT\", and simple AQM algorithm."
|
||||
. " As a scheduler, FQ_CODEL implements several flows (defined below), each having their own CoDel AQM.",
|
||||
"parameters" => array(
|
||||
"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.target") / 1000),
|
||||
"interval" => array("name" => "Interval (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.interval") / 1000),
|
||||
"quantum" => array("name" => "quantum", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.quantum")),
|
||||
"limit" => array("name" => "limit", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.limit")),
|
||||
"flows" => array("name" => "flows", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.flows"))
|
||||
),
|
||||
"ecn" => true
|
||||
),
|
||||
"fq_pie" => array(
|
||||
"name" => "FQ_PIE",
|
||||
"parameter_format" => "type fq_pie target %sms tupdate %sms alpha %s beta %s max_burst %s max_ecnth %s",
|
||||
"description" => "Fair Queue Proportional Integral controller Enhanced AQM (FQ_PIE) is similar to FQ_CODEL but uses a slightly different algorithm.",
|
||||
"parameters" => array(
|
||||
"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.target") / 1000),
|
||||
"tupdate" => array("name" => "Interval (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.tupdate") / 1000),
|
||||
"alpha" => array("name" => "alpha", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.alpha")),
|
||||
"beta" => array("name" => "beta", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.beta")),
|
||||
"max_burst" => array("name" => "max_burst", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.max_burst")),
|
||||
"max_ecnth" => array("name" => "max_ecnth", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.max_ecnth"))
|
||||
),
|
||||
"ecn" => true
|
||||
)
|
||||
);
|
||||
}
|
||||
// list of AQMs that dummynet supports
|
||||
function getAQMs() {
|
||||
return array(
|
||||
"droptail" => array(
|
||||
"name" => "Tail Drop",
|
||||
"description" => "Tail Drop is a fundamental queue management algorithm which drops inbound packets once the queue is full.",
|
||||
"parameter_format" => "droptail",
|
||||
"parameters" => array(),
|
||||
"ecn" => false
|
||||
),
|
||||
"codel" => array(
|
||||
"name" => "CoDel",
|
||||
"description" => "CoDel is a novel \"no knobs\", \"just works\", \"handles variable bandwidth and RTT\", and simple AQM algorithm.",
|
||||
"parameter_format" => "codel target %sms interval %sms",
|
||||
"parameters" => array(
|
||||
"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.codel.target") / 1000),
|
||||
"interval" => array("name" => "Interval (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.codel.interval") / 1000),
|
||||
),
|
||||
"ecn" => true
|
||||
),
|
||||
"pie" => array(
|
||||
"name" => "PIE",
|
||||
"description" => "Proportional Integral controller Enhanced AQM (PIE) is similar to CoDel but uses a slightly different algorithm.",
|
||||
"parameter_format" => "pie target %sms tupdate %sms alpha %s beta %s max_burst %s max_ecnth %s",
|
||||
"parameters" => array(
|
||||
"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.target") / 1000),
|
||||
"tupdate" => array("name" => "Interval (ms)", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.tupdate") / 1000),
|
||||
"alpha" => array("name" => "alpha", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.alpha")),
|
||||
"beta" => array("name" => "beta", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.beta")),
|
||||
"max_burst" => array("name" => "max_burst", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.max_burst")),
|
||||
"max_ecnth" => array("name" => "max_ecnth", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.max_ecnth"))
|
||||
),
|
||||
"ecn" => true
|
||||
),
|
||||
"red" => array(
|
||||
"name" => "Random Early Detection (RED)",
|
||||
"description" => "Random Early Detection (RED) drops packets based on probability, which increases as the queue increases in size.",
|
||||
"parameter_format" => "red %s/%s/%s/%s",
|
||||
"parameters" => array(
|
||||
"w_q" => array("name" => "w_q", "type" => "number", "default" => 1),
|
||||
"min_th" => array("name" => "min_th", "type" => "number", "default" => 0),
|
||||
"max_th" => array("name" => "max_th", "type" => "number", "default" => 1),
|
||||
"max_p" => array("name" => "max_p", "type" => "number", "default" => 1)
|
||||
),
|
||||
"ecn" => true
|
||||
),
|
||||
"gred" => array(
|
||||
"name" => "Gentle Random Early Detection (GRED)",
|
||||
"description" => "Gentle Random Early Detection (GRED) drops packets based on probability, which increases as the queue increases in size.",
|
||||
"parameter_format" => "gred %s/%s/%s/%s",
|
||||
"parameters" => array(
|
||||
"w_q" => array("name" => "w_q", "type" => "number", "default" => 1),
|
||||
"min_th" => array("name" => "min_th", "type" => "number", "default" => 0),
|
||||
"max_th" => array("name" => "max_th", "type" => "number", "default" => 1),
|
||||
"max_p" => array("name" => "max_p", "type" => "number", "default" => 1)
|
||||
),
|
||||
"ecn" => true
|
||||
)
|
||||
);
|
||||
}
|
||||
// used to map above
|
||||
function array_map_assoc(callable $f, array $a) {
|
||||
return array_column(array_map($f, array_keys($a), $a), 1, 0);
|
||||
}
|
||||
function build_queue_params($array, $selected, $params, $id) {
|
||||
$form .= '<div id="params_' . $id . '">';
|
||||
|
||||
$selectedAlgorithm = $array[$selected];
|
||||
|
||||
if ($selectedAlgorithm) {
|
||||
$form .= '<span class="help-block">' . gettext($selectedAlgorithm['description']) . '</span><br/>';
|
||||
}
|
||||
|
||||
$parameters = $selectedAlgorithm["parameters"];
|
||||
|
||||
if (!$parameters || sizeof($parameters) <= 0) {
|
||||
if (!$selectedAlgorithm) {
|
||||
$form .= 'No parameters for selected algorithm.';
|
||||
} else {
|
||||
$form .= 'No parameters for ' . $selectedAlgorithm["name"] . '.';
|
||||
}
|
||||
} else {
|
||||
$form .= '<div class="table-responsive">';
|
||||
$form .= '<table id="maintable" class="table table-hover table-striped">';
|
||||
$form .= "<thead><tr>";
|
||||
$form .= "<th>Parameter</th>";
|
||||
$form .= "<th>Value</th>";
|
||||
$form .= "</tr></thead>";
|
||||
$form .= "<tbody>";
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
$form .= '<tr>';
|
||||
|
||||
// get current value, or default.
|
||||
$currentValue = $params[$key];
|
||||
if (!$currentValue || $currentValue == '') {
|
||||
$currentValue = $value["default"]; // default to default
|
||||
}
|
||||
|
||||
$form .= "<td class=\"col-xs-4\">" . htmlspecialchars($key) . "</td>";
|
||||
$form .= "<td class=\"col-xs-8\"><input class=\"form-control\" type=" . gettext($value["type"])
|
||||
. " name=\"param_" . $selected . "_" . $key . "\" placeholder=\"" .
|
||||
htmlspecialchars($value["default"]) . "\" value=\"" . htmlspecialchars($currentValue) . "\"/></td>";
|
||||
|
||||
$form .= '</tr>';
|
||||
}
|
||||
|
||||
$form .= "</tbody></table></div><br />";
|
||||
}
|
||||
|
||||
$form .= '</div>';
|
||||
$form .= '<script type="text/javascript">';
|
||||
$form .= 'events.push(function() {$("#' . $id . '").change(function() {$("#params_' .
|
||||
gettext($id) . '").html("Save this limiter to see algorithm parameters.");})});</script>';
|
||||
|
||||
return($form);
|
||||
}
|
||||
function FormatParameters($format_string, $params) {
|
||||
return vsprintf($format_string, $params);
|
||||
}
|
||||
/* End limiter patch */
|
||||
|
||||
function get_queue_stats() {
|
||||
// due to sysutils\qstats not providing accurate stats with 'currently' valid numbers
|
||||
// in its current implementation this php function does the job for now..
|
||||
@ -3459,6 +3653,56 @@ class dnpipe_class extends dummynet_class {
|
||||
var $delay;
|
||||
var $qbandwidth = array();
|
||||
var $qbandwidthtype;
|
||||
|
||||
/* Limiter queue patch */
|
||||
var $ecn; // ecn 'on' or 'off'
|
||||
var $aqm; // key to aqm_map
|
||||
var $aqm_params = array(); // AQM params
|
||||
var $scheduler; // key to scheduler_map
|
||||
var $scheduler_params = array(); // AQM params
|
||||
function GetAQM() {
|
||||
return $this->aqm;
|
||||
}
|
||||
function SetAQM($aqm) {
|
||||
$this->aqm = $aqm;
|
||||
}
|
||||
function GetAQMParameters() {
|
||||
return $this->aqm_params;
|
||||
}
|
||||
function GetAQMParameter($parameter) {
|
||||
return $this->aqm_params[$parameter];
|
||||
}
|
||||
function SetAQMParameter($key, $value) {
|
||||
return $this->aqm_params[$key] = $value;
|
||||
}
|
||||
function SetAQMParameters($params) {
|
||||
$this->aqm_params = $params;
|
||||
}
|
||||
function GetECN() {
|
||||
return $this->ecn;
|
||||
}
|
||||
function SetECN($ecn) {
|
||||
$this->ecn = $ecn;
|
||||
}
|
||||
function GetScheduler() {
|
||||
return $this->scheduler;
|
||||
}
|
||||
function SetScheduler($scheduler) {
|
||||
$this->scheduler = $scheduler;
|
||||
}
|
||||
function GetSchedulerParameters() {
|
||||
return $this->scheduler_params;
|
||||
}
|
||||
function SetSchedulerParameters($params) {
|
||||
$this->scheduler_params = $params;
|
||||
}
|
||||
function SetSchedulerParameter($key, $value) {
|
||||
$this->scheduler_params[$key] = $value;
|
||||
}
|
||||
function GetSchedulerParameter($key) {
|
||||
return $this->scheduler_params[$key];
|
||||
}
|
||||
/* End limiter queue patch */
|
||||
|
||||
/* This is here to help on form building and building rules/lists */
|
||||
var $subqueues = array();
|
||||
@ -3479,6 +3723,7 @@ class dnpipe_class extends dummynet_class {
|
||||
}
|
||||
unset_dn_object_by_reference($this->GetLink());
|
||||
@pfSense_ipfw_pipe("pipe delete " . $this->GetNumber());
|
||||
@pfSense_ipfw_pipe("sched delete " . $this->GetNumber());
|
||||
}
|
||||
function GetBandwidth() {
|
||||
return $this->qbandwidth;
|
||||
@ -3590,6 +3835,20 @@ class dnpipe_class extends dummynet_class {
|
||||
if ($data['delay'] && (!is_numeric($data['delay']))) {
|
||||
$input_errors[] = gettext("Delay must be an integer.");
|
||||
}
|
||||
|
||||
/* Limiter patch */
|
||||
$selectedScheduler = getSchedulers()[$data['sched']];
|
||||
if (!$selectedScheduler) {
|
||||
$input_errors[] = gettext("Selected scheduler not recognized.");
|
||||
}
|
||||
$selectedAqm = getAQMs()[$data['aqm']];
|
||||
if (!$selectedAqm) {
|
||||
$input_errors[] = gettext("Selected AQM not recognized.");
|
||||
}
|
||||
if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedScheduler["ecn"] && !$selectedAqm["ecn"]) {
|
||||
$input_errors[] = gettext("Explicit Congestion Notification is selected, but neither " . $selectedAqm["name"] . " nor " . $selectedScheduler["name"] . " support it.");
|
||||
}
|
||||
/* End limiter patch */
|
||||
}
|
||||
|
||||
function ReadConfig(&$q) {
|
||||
@ -3669,7 +3928,48 @@ class dnpipe_class extends dummynet_class {
|
||||
$this->SetDescription("");
|
||||
}
|
||||
$this->SetEnabled($q['enabled']);
|
||||
|
||||
|
||||
/* Limiter patch */
|
||||
if (isset($q['aqm']) && $q['aqm'] <> "") {
|
||||
$this->SetAQM($q['aqm']);
|
||||
} else {
|
||||
$this->SetAQM('droptail');
|
||||
}
|
||||
// parse AQM arguments
|
||||
$aqm_map = getAQMs();
|
||||
$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
|
||||
if (is_array($selectedParameters)) {
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetAQM() . '_' . $key;
|
||||
if (isset($q[$config_key]) && $q[$config_key] <> "") {
|
||||
$this->SetAQMParameter($key, $q[$config_key]);
|
||||
} else {
|
||||
$this->SetAQMParameter($key, $value["default"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($q['sched']) && $q['sched'] <> "") {
|
||||
$this->SetScheduler($q['sched']);
|
||||
} else {
|
||||
$this->SetScheduler('fifo');
|
||||
}
|
||||
$scheduler_map = getSchedulers();
|
||||
$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
|
||||
if (is_array($selectedParameters)) {
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
|
||||
if (isset($q[$config_key]) && $q[$config_key] <> "") {
|
||||
$this->SetSchedulerParameter($key, $q[$config_key]);
|
||||
} else {
|
||||
$this->SetSchedulerParameter($key, $value["default"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ecn flag
|
||||
$this->SetECN($q['ecn']);
|
||||
/* End limiter patch */
|
||||
}
|
||||
|
||||
function build_tree() {
|
||||
@ -3751,13 +4051,38 @@ class dnpipe_class extends dummynet_class {
|
||||
}
|
||||
$this->build_mask_rules($pfq_rule);
|
||||
|
||||
/* Limiter patch */
|
||||
$selectedAQM = getAQMs()[$this->getAQM()];
|
||||
if ($selectedAQM) {
|
||||
$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
|
||||
if ($selectedAQM["ecn"]) {
|
||||
if ($this->getECN() == 'on') {
|
||||
$pfq_rule .= ' ecn';
|
||||
} else {
|
||||
$pfq_rule .= ' noecn';
|
||||
}
|
||||
}
|
||||
}
|
||||
$selectedScheduler = getSchedulers()[$this->getScheduler()];
|
||||
if ($selectedScheduler) {
|
||||
$pfq_rule .= "\nsched ". $this->GetNumber() . " config pipe " . $this->GetNumber() . " " . FormatParameters($selectedScheduler["parameter_format"], $this->GetSchedulerParameters());
|
||||
if ($selectedScheduler["ecn"]) {
|
||||
if ($this->getECN() == 'on') {
|
||||
$pfq_rule .= ' ecn';
|
||||
} else {
|
||||
$pfq_rule .= ' noecn';
|
||||
}
|
||||
}
|
||||
}
|
||||
$pfq_rule .= "\n";
|
||||
|
||||
/* End patch */
|
||||
|
||||
if (!empty($this->subqueues) && count($this->subqueues) > 0) {
|
||||
foreach ($this->subqueues as $q) {
|
||||
$pfq_rule .= $q->build_rules();
|
||||
}
|
||||
}
|
||||
|
||||
$pfq_rule .= " \n";
|
||||
|
||||
return $pfq_rule;
|
||||
@ -4010,6 +4335,60 @@ EOD;
|
||||
|
||||
$sform->add($section);
|
||||
|
||||
/* Begin limiter patch */
|
||||
$aqm_map = getAQMs();
|
||||
$scheduler_map = getSchedulers();
|
||||
|
||||
$section = new Form_Section('Queue');
|
||||
$section->addInput(new Form_Select(
|
||||
'aqm',
|
||||
'Queue Management Algorithm',
|
||||
$this->GetAQM(),
|
||||
array_map_assoc(function ($k, $v) {
|
||||
return [$k, $v["name"]];
|
||||
}, $aqm_map)
|
||||
))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside the limiter, ' .
|
||||
'when it becomes full or gets close to becoming full, with the goal of reducing ' .
|
||||
'network congestion.');
|
||||
|
||||
$section->addInput(new Form_StaticText(
|
||||
'',
|
||||
build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
|
||||
))->setHelp('Specifies the queue management algorithm parameters.');
|
||||
|
||||
$section->addInput(new Form_Select(
|
||||
'sched',
|
||||
'Scheduler',
|
||||
$this->GetScheduler(),
|
||||
array_map_assoc(function ($k, $v) {
|
||||
return [$k, $v["name"]];
|
||||
}, $scheduler_map)
|
||||
))->setHelp('The scheduler manages the sequence of network packets in the limiter\'s queue.');
|
||||
|
||||
$section->addInput(new Form_StaticText(
|
||||
'',
|
||||
build_queue_params($scheduler_map, $this->GetScheduler(), $this->GetSchedulerParameters(), "sched")
|
||||
))->setHelp('Specifies the scheduler parameters.');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'qlimit',
|
||||
'Queue length',
|
||||
'number',
|
||||
$this->GetQlimit()
|
||||
))->setHelp('Specifies the length of the limiter\'s queue, which the scheduler and AQM are responsible for. ' .
|
||||
'This field may be left empty.');
|
||||
|
||||
$section->addInput(new Form_Checkbox(
|
||||
'ecn',
|
||||
'ECN',
|
||||
'Enable Explicit Congestion Notification (ECN)',
|
||||
($this->GetECN() == "on"),
|
||||
'on'
|
||||
))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
|
||||
|
||||
$sform->add($section);
|
||||
/* End limiter patch */
|
||||
|
||||
$section = new Form_Section('Advanced Options');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
@ -4028,14 +4407,6 @@ EOD;
|
||||
))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
|
||||
'A value of 0.001 means one packet in 1000 gets dropped.');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'qlimit',
|
||||
'Queue size (slots)',
|
||||
'number',
|
||||
$this->GetQlimit()
|
||||
))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
|
||||
'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'buckets',
|
||||
'Bucket size (slots)',
|
||||
@ -4075,6 +4446,24 @@ EOD;
|
||||
$cflink['maskbits'] = $mask['bits'];
|
||||
$cflink['maskbitsv6'] = $mask['bitsv6'];
|
||||
$cflink['delay'] = $this->GetDelay();
|
||||
|
||||
/* Limiter queue patch */
|
||||
$cflink['sched'] = $this->GetScheduler();
|
||||
$scheduler_map = getSchedulers();
|
||||
$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
|
||||
$cflink[$config_key] = $this->GetSchedulerParameter($key);
|
||||
}
|
||||
$cflink['aqm'] = $this->GetAQM();
|
||||
$aqm_map = GetAQMs();
|
||||
$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetAQM() . '_' . $key;
|
||||
$cflink[$config_key] = $this->GetAQMParameter($key);
|
||||
}
|
||||
$cflink['ecn'] = $this->GetECN();
|
||||
/* End limiter queue patch */
|
||||
}
|
||||
|
||||
}
|
||||
@ -4082,6 +4471,35 @@ EOD;
|
||||
class dnqueue_class extends dummynet_class {
|
||||
var $pipeparent;
|
||||
var $weight;
|
||||
/* Limiter queue patch */
|
||||
var $ecn; // ecn 'on' or 'off'
|
||||
var $aqm; // key to aqm_map
|
||||
var $aqm_params = array(); // AQM params
|
||||
function GetAQM() {
|
||||
return $this->aqm;
|
||||
}
|
||||
function SetAQM($aqm) {
|
||||
$this->aqm = $aqm;
|
||||
}
|
||||
function GetAQMParameters() {
|
||||
return $this->aqm_params;
|
||||
}
|
||||
function GetAQMParameter($parameter) {
|
||||
return $this->aqm_params[$parameter];
|
||||
}
|
||||
function SetAQMParameter($key, $value) {
|
||||
return $this->aqm_params[$key] = $value;
|
||||
}
|
||||
function SetAQMParameters($params) {
|
||||
$this->aqm_params = $params;
|
||||
}
|
||||
function GetECN() {
|
||||
return $this->ecn;
|
||||
}
|
||||
function SetECN($ecn) {
|
||||
$this->ecn = $ecn;
|
||||
}
|
||||
/* End limiter queue patch */
|
||||
|
||||
function GetWeight() {
|
||||
return $this->weight;
|
||||
@ -4109,6 +4527,17 @@ class dnqueue_class extends dummynet_class {
|
||||
|
||||
function validate_input($data, &$input_errors) {
|
||||
parent::validate_input($data, $input_errors);
|
||||
|
||||
|
||||
/* Limiter patch */
|
||||
$selectedAqm = getAQMs()[$data['aqm']];
|
||||
if (!$selectedAqm) {
|
||||
$input_errors[] = gettext("Selected AQM not recognized.");
|
||||
}
|
||||
if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedAqm["ecn"]) {
|
||||
$input_errors[] = gettext("Explicit Congestion Notification is selected, but " . $selectedAqm["name"] . " does not support it.");
|
||||
}
|
||||
/* End limiter patch */
|
||||
|
||||
if ($data['weight'] && ((!is_numeric($data['weight'])) ||
|
||||
($data['weight'] < 1 && $data['weight'] > 100))) {
|
||||
@ -4189,6 +4618,28 @@ class dnqueue_class extends dummynet_class {
|
||||
$this->SetDescription("");
|
||||
}
|
||||
$this->SetEnabled($q['enabled']);
|
||||
|
||||
/* Limiter patch */
|
||||
if (isset($q['aqm']) && $q['aqm'] <> "") {
|
||||
$this->SetAQM($q['aqm']);
|
||||
} else {
|
||||
$this->SetAQM('droptail');
|
||||
}
|
||||
// parse AQM arguments
|
||||
$aqm_map = getAQMs();
|
||||
$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetAQM() . '_' . $key;
|
||||
if (isset($q[$config_key]) && $q[$config_key] <> "") {
|
||||
$this->SetAQMParameter($key, $q[$config_key]);
|
||||
} else {
|
||||
$this->SetAQMParameter($key, $value["default"]);
|
||||
}
|
||||
}
|
||||
|
||||
// ecn flag
|
||||
$this->SetECN($q['ecn']);
|
||||
/* End limiter patch */
|
||||
}
|
||||
|
||||
function build_tree() {
|
||||
@ -4217,6 +4668,21 @@ class dnqueue_class extends dummynet_class {
|
||||
$pfq_rule .= " buckets " . $this->GetBuckets();
|
||||
}
|
||||
$this->build_mask_rules($pfq_rule);
|
||||
|
||||
/* Limiter patch */
|
||||
$selectedAQM = getAQMs()[$this->getAQM()];
|
||||
if ($selectedAQM) {
|
||||
$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
|
||||
if ($selectedAQM["ecn"]) {
|
||||
if ($this->getECN() == 'on') {
|
||||
$pfq_rule .= ' ecn';
|
||||
} else {
|
||||
$pfq_rule .= ' noecn';
|
||||
}
|
||||
}
|
||||
}
|
||||
/* End patch */
|
||||
|
||||
$pfq_rule .= "\n";
|
||||
|
||||
return $pfq_rule;
|
||||
@ -4313,6 +4779,44 @@ class dnqueue_class extends dummynet_class {
|
||||
))->setHelp('A description may be entered here for administrative reference (not parsed).');
|
||||
|
||||
$sform->add($section);
|
||||
|
||||
/* Begin limiter patch */
|
||||
$aqm_map = getAQMs();
|
||||
|
||||
$section = new Form_Section('Queue');
|
||||
$section->addInput(new Form_Select(
|
||||
'aqm',
|
||||
'Queue Management Algorithm',
|
||||
$this->GetAQM(),
|
||||
array_map_assoc(function ($k, $v) {
|
||||
return [$k, $v["name"]];
|
||||
}, $aqm_map)
|
||||
))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside this limiter\'s queue, ' .
|
||||
'when it becomes full or gets close to becoming full, with the goal of reducing ' .
|
||||
'network congestion.');
|
||||
|
||||
$section->addInput(new Form_StaticText(
|
||||
'',
|
||||
build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
|
||||
))->setHelp('Specifies the queue management algorithm parameters.');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'qlimit',
|
||||
'Queue length',
|
||||
'number',
|
||||
$this->GetQlimit()
|
||||
))->setHelp('Specifies the length of this queue, which the AQM is responsible for. This field may be left empty.');
|
||||
|
||||
$section->addInput(new Form_Checkbox(
|
||||
'ecn',
|
||||
'ECN',
|
||||
'Enable Explicit Congestion Notification (ECN)',
|
||||
($this->GetECN() == "on"),
|
||||
'on'
|
||||
))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
|
||||
|
||||
$sform->add($section);
|
||||
/* End limiter patch */
|
||||
|
||||
$section = new Form_Section('Advanced Options');
|
||||
|
||||
@ -4333,15 +4837,7 @@ class dnqueue_class extends dummynet_class {
|
||||
['step' => '0.001', 'min' => '0.000']
|
||||
))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
|
||||
'A value of 0.001 means one packet in 1000 gets dropped');
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'qlimit',
|
||||
'Queue size (slots)',
|
||||
'number',
|
||||
$this->GetQlimit()
|
||||
))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
|
||||
'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
|
||||
|
||||
|
||||
$section->addInput(new Form_Input(
|
||||
'buckets',
|
||||
'Bucket size (slots)',
|
||||
@ -4381,6 +4877,17 @@ class dnqueue_class extends dummynet_class {
|
||||
$cflink['mask'] = $mask['type'];
|
||||
$cflink['maskbits'] = $mask['bits'];
|
||||
$cflink['maskbitsv6'] = $mask['bitsv6'];
|
||||
|
||||
/* Limiter queue patch */
|
||||
$cflink['aqm'] = $this->GetAQM();
|
||||
$aqm_map = GetAQMs();
|
||||
$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
|
||||
foreach ($selectedParameters as $key => $value) {
|
||||
$config_key = 'param_' . $this->GetAQM() . '_' . $key;
|
||||
$cflink[$config_key] = $this->GetAQMParameter($key);
|
||||
}
|
||||
$cflink['ecn'] = $this->GetECN();
|
||||
/* End limiter queue patch */
|
||||
}
|
||||
}
|
||||
|
||||
@ -4776,4 +5283,4 @@ function interface_has_queue($if) {
|
||||
|
||||
return false;
|
||||
}
|
||||
?>
|
||||
?>
|
||||
Loading…
Reference in New Issue
Block a user