";
return $form;
}
/*
* For requesting the parameters of the root queues
* to the user like the traffic wizard does.
*/
function build_form() {
$form = "
";
$form .= " GetTbrConfig();
$form .= "\" />";
$form .= " ";
$form .= gettext("Adjusts the size, in bytes, of the token bucket regulator. "
. "If not specified, heuristics based on the interface "
. "bandwidth are used to determine the size.");
$form .= "
";
$form .= "GetInterface() . "\" />";
$form .= "GetQname()."\" />";
return $form;
}
function update_altq_queue_data(&$data) {
$this->ReadConfig($data);
}
/*
* Should call on each of it queues and subqueues
* the same function much like build_rules();
*/
function wconfig() {
$cflink = &get_reference_to_me_in_config($this->GetLink());
if (!is_array($cflink))
$cflink = array();
$cflink['interface'] = $this->GetInterface();
$cflink['name'] = $this->GetQname();
$cflink['scheduler'] = $this->GetScheduler();
$cflink['bandwidth'] = $this->GetBandwidth();
$cflink['bandwidthtype'] = $this->GetBwscale();
$cflink['qlimit'] = trim($this->GetQlimit());
if (empty($cflink['qlimit']))
unset($cflink['qlimit']);
$cflink['tbrconfig'] = trim($this->GetTbrConfig());
if (empty($cflink['tbrconfig']))
unset($cflink['tbrconfig']);
$cflink['enabled'] = $this->GetEnabled();
if (empty($cflink['enabled']))
unset($cflink['enabled']);
}
}
class priq_queue {
var $qname;
var $qinterface;
var $qlimit;
var $qpriority;
var $description;
var $isparent;
var $qbandwidth;
var $qbandwidthtype;
var $qdefault = "";
var $qrio = "";
var $qred = "";
var $qcodel = "";
var $qecn = "";
var $qack;
var $qenabled = "";
var $qparent;
var $link;
var $available_bw; /* in b/s */
/* This is here to help with form building and building rules/lists */
var $subqueues = array();
/* Accesor functions */
function GetAvailableBandwidth() {
return $this->available_bw;
}
function SetAvailableBandwidth($bw) {
$this->available_bw = $bw;
}
function SetLink($link) {
$this->link = $link;
}
function GetLink() {
return $this->link;
}
function &GetParent() {
return $this->qparent;
}
function SetParent(&$parent) {
$this->qparent = &$parent;
}
function GetEnabled() {
return $this->qenabled;
}
function SetEnabled($value) {
$this->qenabled = $value;
}
function CanHaveChildren() {
return false;
}
function CanBeDeleted() {
return true;
}
function GetQname() {
return $this->qname;
}
function SetQname($name) {
$this->qname = trim($name);
}
function GetBandwidth() {
return $this->qbandwidth;
}
function SetBandwidth($bandwidth) {
$this->qbandwidth = $bandwidth;
}
function GetInterface() {
return $this->qinterface;
}
function SetInterface($name) {
$this->qinterface = trim($name);
}
function GetQlimit() {
return $this->qlimit;
}
function SetQlimit($limit) {
$this->qlimit = $limit;
}
function GetQpriority() {
return $this->qpriority;
}
function SetQpriority($priority) {
$this->qpriority = $priority;
}
function GetDescription() {
return $this->description;
}
function SetDescription($str) {
$this->description = trim($str);
}
function GetFirstime() {
return $this->firsttime;
}
function SetFirsttime($number) {
$this->firsttime = $number;
}
function GetBwscale() {
return $this->qbandwidthtype;
}
function SetBwscale($scale) {
$this->qbandwidthtype = $scale;
}
function GetDefaultQueuePresent() {
if ($this->GetDefault())
return true;
if (!empty($this->subqueues)) {
foreach ($this->subqueues as $q) {
if ($q->GetDefault())
return true;
}
}
return false;
}
function GetDefault() {
return $this->qdefault;
}
function SetDefault($value = false) {
$this->qdefault = $value;
}
function GetCodel() {
return $this->codel;
}
function SetCodel($codel = false) {
$this->codel = $codel;
}
function GetRed() {
return $this->qred;
}
function SetRed($red = false) {
$this->qred = $red;
}
function GetRio() {
return $this->qrio;
}
function SetRio($rio = false) {
$this->qrio = $rio;
}
function GetEcn() {
return $this->qecn;
}
function SetEcn($ecn = false) {
$this->qecn = $ecn;
}
function GetAck() {
return $this->qack;
}
function SetAck($ack = false) {
$this->qack = $ack;
}
function build_javascript() {
$javascript = "";
return $javascript;
}
function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
/*
* Currently this will not be called unless we decide to clone a whole
* queue tree on the 'By Queues' view or support drag&drop on the tree/list
*/
function copy_queue($interface, &$cflink) {
$cflink['name'] = $this->GetQname();
$cflink['interface'] = $interface;
$cflink['qlimit'] = $this->GetQlimit();
$cflink['priority'] = $this->GetQpriority();
$cflink['description'] = $this->GetDescription();
$cflink['enabled'] = $this->GetEnabled();
$cflink['default'] = $this->GetDefault();
$cflink['red'] = $this->GetRed();
$cflink['codel'] = $this->GetCodel();
$cflink['rio'] = $this->GetRio();
$cflink['ecn'] = $this->GetEcn();
if (is_array($this->subqueues)) {
$cflinkp['queue'] = array();
foreach ($this->subqueues as $q) {
$cflink['queue'][$q->GetQname()] = array();
$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
}
}
}
function clean_queue($sched) {
clean_child_queues($sched, $this->GetLink());
if (is_array($this->subqueues)) {
foreach ($this->subqueues as $q)
$q->clean_queue($sched);
}
}
function &get_queue_list(&$qlist) {
$qlist[$this->GetQname()] = & $this;
if (is_array($this->subqueues)) {
foreach ($this->subqueues as $queue)
$queue->get_queue_list($qlist);
}
}
function delete_queue() {
unref_on_altq_queue_list($this->GetQname());
cleanup_queue_from_rules($this->GetQname());
unset_object_by_reference($this->GetLink());
}
function delete_all() {
if (count($this->subqueues)) {
foreach ($this->subqueues as $q) {
$q->delete_all();
unset_object_by_reference($q->GetLink());
unset($q);
}
unset($this->subqueues);
}
}
function &find_queue($interface, $qname) {
if ($qname == $this->GetQname())
return $this;
}
function find_parentqueue($interface, $qname) { return; }
function validate_input($data, &$input_errors) {
$reqdfields[] = "name";
$reqdfieldsn[] = gettext("Name");
shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
if ($data['bandwidth'] && (!is_numeric($data['bandwidth'])))
$input_errors[] = "Bandwidth must be an integer.";
if ($data['bandwidth'] < 0)
$input_errors[] = "Bandwidth cannot be negative.";
if ($data['priority'] && (!is_numeric($data['priority'])
|| ($data['priority'] < 1) || ($data['priority'] > 15))) {
$input_errors[] = gettext("The priority must be an integer between 1 and 15.");
}
if ($data['qlimit'] && (!is_numeric($data['qlimit'])))
$input_errors[] = gettext("Queue limit must be an integer");
if ($data['qlimit'] < 0)
$input_errors[] = gettext("Queue limit must be positive");
if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname']))
$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name']))
$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
$default = $this->GetDefault();
if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default))
$input_errors[] = gettext("Only one default queue per interface is allowed.");
}
function ReadConfig(&$q) {
if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
$this->SetQname($q['newname']);
} else if (!empty($q['newname'])) {
$this->SetQname($q['newname']);
} else if (isset($q['name']))
$this->SetQname($q['name']);
if (isset($q['interface']))
$this->SetInterface($q['interface']);
$this->SetBandwidth($q['bandwidth']);
if ($q['bandwidthtype'] <> "")
$this->SetBwscale($q['bandwidthtype']);
if (!empty($q['qlimit']))
$this->SetQlimit($q['qlimit']);
else
$this->SetQlimit(""); // Default
if (!empty($q['priority']))
$this->SetQPriority($q['priority']);
else
$this->SetQpriority("");
if (!empty($q['description']))
$this->SetDescription($q['description']);
else
$this->SetDescription("");
if (!empty($q['red']))
$this->SetRed($q['red']);
else
$this->SetRed();
if (!empty($q['codel']))
$this->SetCodel($q['codel']);
else
$this->SetCodel();
if (!empty($q['rio']))
$this->SetRio($q['rio']);
else
$this->SetRio();
if (!empty($q['ecn']))
$this->SetEcn($q['ecn']);
else
$this->SetEcn();
if (!empty($q['default']))
$this->SetDefault($q['default']);
else
$this->SetDefault();
if (!empty($q['enabled']))
$this->SetEnabled($q['enabled']);
else
$this->SetEnabled("");
}
function build_tree() {
$tree = "
";
return $tree;
}
/* Should return something like:
* queue $qname on $qinterface bandwidth ....
*/
function build_rules(&$default = false) {
$pfq_rule = " queue ". $this->qname;
if ($this->GetInterface())
$pfq_rule .= " on ".get_real_interface($this->GetInterface());
$tmpvalue = $this->GetQpriority();
if (!empty($tmpvalue))
$pfq_rule .= " priority ".$this->GetQpriority();
$tmpvalue = $this->GetQlimit();
if (!empty($tmpvalue))
$pfq_rule .= " qlimit " . $this->GetQlimit();
if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
$pfq_rule .= " priq ( ";
$tmpvalue = $this->GetRed();
if (!empty($tmpvalue)) {
$comma = 1;
$pfq_rule .= " red ";
}
$tmpvalue = $this->GetRio();
if (!empty($tmpvalue)) {
if ($comma)
$pfq_rule .= " ,";
$comma = 1;
$pfq_rule .= " rio ";
}
$tmpvalue = $this->GetEcn();
if (!empty($tmpvalue)) {
if ($comma)
$pfq_rule .= " ,";
$comma = 1;
$pfq_rule .= " ecn ";
}
$tmpvalue = $this->GetCodel();
if (!empty($tmpvalue)) {
if ($comma)
$pfq_rule .= " ,";
$comma = 1;
$pfq_rule .= " codel ";
}
$tmpvalue = $this->GetDefault();
if (!empty($tmpvalue)) {
if ($comma)
$pfq_rule .= " ,";
$pfq_rule .= " default ";
$default = true;
}
$pfq_rule .= " ) ";
}
$pfq_rule .= " \n";
return $pfq_rule;
}
/*
* To return the html form to show to user
* for getting the parameters.
* Should do even for first time when the
* object is created and later when we may
* need to update it.
*/
function build_form() {
$form = "
";
$form .= "GetQname());
$form .= "\" />";
$form .= "GetQname());
$form .= "\" />";
$form .= " " . gettext("Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.");
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Priority") . "
";
$form .= "
GetQpriority());
$form .= "\" />";
$form .= " " . gettext("For hfsc, the range is 0 to 7. The default is 1. Hfsc queues with a higher priority are preferred in the case of overload.") . "
" . gettext("The bandwidth share of a backlogged queue - this overrides priority.") . "
";
$form .= "
";
$form .= gettext("The format for service curve specifications is (m1, d, m2). m2 controls "
. "the bandwidth assigned to the queue. m1 and d are optional and can be "
. "used to control the initial bandwidth assignment. For the first d milliseconds the queue gets the bandwidth given as m1, afterwards the value "
. "given in m2.");
$form .= "
";
$form .= "
";
return $form;
}
function update_altq_queue_data(&$data) {
$this->ReadConfig($data);
}
function wconfig() {
$cflink =& get_reference_to_me_in_config($this->GetLink());
if (!is_array($cflink))
$cflink = array();
$cflink['name'] = $this->GetQname();
$cflink['interface'] = $this->GetInterface();
$cflink['qlimit'] = trim($this->GetQlimit());
if (empty($cflink['qlimit']))
unset($cflink['qlimit']);
$cflink['priority'] = $this->GetQpriority();
if (empty($cflink['priority']))
unset($cflink['priority']);
$cflink['description'] = $this->GetDescription();
if (empty($cflink['description']))
unset($cflink['description']);
$cflink['bandwidth'] = $this->GetBandwidth();
$cflink['bandwidthtype'] = $this->GetBwscale();
$cflink['enabled'] = $this->GetEnabled();
if (empty($cflink['enabled']))
unset($cflink['enabled']);
$cflink['default'] = $this->GetDefault();
if (empty($cflink['default']))
unset($cflink['default']);
$cflink['red'] = trim($this->GetRed());
if (empty($cflink['red']))
unset($cflink['red']);
$cflink['rio'] = $this->GetRio();
if (empty($cflink['rio']))
unset($cflink['rio']);
$cflink['ecn'] = trim($this->GetEcn());
if (empty($cflink['ecn']))
unset($cflink['ecn']);
$cflink['codel'] = trim($this->GetCodel());
if (empty($cflink['codel']))
unset($cflink['codel']);
if ($this->GetLinkshare() <> "") {
if ($this->GetL_m1() <> "") {
$cflink['linkshare1'] = $this->GetL_m1();
$cflink['linkshare2'] = $this->GetL_d();
$cflink['linkshare'] = "on";
} else {
unset($cflink['linkshare']);
unset($cflink['linkshare1']);
unset($cflink['linkshare2']);
}
if ($this->GetL_m2() <> "") {
$cflink['linkshare3'] = $this->GetL_m2();
$cflink['linkshare'] = "on";
} else {
unset($cflink['linkshare']);
unset($cflink['linkshare3']);
}
} else {
unset($cflink['linkshare']);
unset($cflink['linkshare1']);
unset($cflink['linkshare2']);
unset($cflink['linkshare3']);
}
if ($this->GetRealtime() <> "") {
if ($this->GetR_m1() <> "") {
$cflink['realtime1'] = $this->GetR_m1();
$cflink['realtime2'] = $this->GetR_d();
$cflink['realtime'] = "on";
} else {
unset($cflink['realtime']);
unset($cflink['realtime1']);
unset($cflink['realtime2']);
}
if ($this->GetR_m2() <> "") {
$cflink['realtime3'] = $this->GetR_m2();
$cflink['realtime'] = "on";
} else {
unset($cflink['realtime']);
unset($cflink['realtime3']);
}
} else {
unset($cflink['realtime']);
unset($cflink['realtime1']);
unset($cflink['realtime2']);
unset($cflink['realtime3']);
}
if ($this->GetUpperlimit() <> "") {
if ($this->GetU_m1() <> "") {
$cflink['upperlimit1'] = $this->GetU_m1();
$cflink['upperlimit2'] = $this->GetU_d();
$cflink['upperlimit'] = "on";
} else {
unset($cflink['upperlimit']);
unset($cflink['upperlimit1']);
unset($cflink['upperlimit2']);
}
if ($this->GetU_m2() <> "") {
$cflink['upperlimit3'] = $this->GetU_m2();
$cflink['upperlimit'] = "on";
} else {
unset($cflink['upperlimit']);
unset($cflink['upperlimit3']);
}
} else {
unset($cflink['upperlimit']);
unset($cflink['upperlimit1']);
unset($cflink['upperlimit2']);
unset($cflink['upperlimit3']);
}
}
}
class cbq_queue extends priq_queue {
var $qborrow = "";
function GetBorrow() {
return $this->qborrow;
}
function SetBorrow($borrow) {
$this->qborrow = $borrow;
}
function CanHaveChildren() {
return true;
}
function &add_queue($interface, &$qname, &$path, &$input_errors) {
if (!is_array($this->subqueues))
$this->subqueues = array();
$q =& new cbq_queue();
$q->SetInterface($this->GetInterface());
$q->SetParent($this);
$q->ReadConfig($qname);
$q->validate_input($qname, $input_errors);
if (count($input_errors)) {
log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
return $q;
}
switch ($q->GetBwscale()) {
case "%":
$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
break;
default:
$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
break;
}
$q->SetAvailableBandwidth($myBw);
$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
$q->SetEnabled("on");
$q->SetLink($path);
$this->subqueues[$q->GetQName()] = &$q;
ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
if (is_array($qname['queue'])) {
foreach ($qname['queue'] as $key1 => $que) {
array_push($path, $key1);
$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
array_pop($path);
}
}
return $q;
}
function copy_queue($interface, &$cflink) {
$cflink['interface'] = $interface;
$cflink['qlimit'] = trim($this->GetQlimit());
if (empty($clink['qlimit']))
unset($cflink['qlimit']);
$cflink['priority'] = trim($this->GetQpriority());
if (empty($cflink['priority']))
unset($cflink['priority']);
$cflink['name'] = $this->GetQname();
$cflink['description'] = trim($this->GetDescription());
if (empty($cflink['description']))
unset($cflink['description']);
$cflink['bandwidth'] = $this->GetBandwidth();
$cflink['bandwidthtype'] = $this->GetBwscale();
$cflink['enabled'] = trim($this->GetEnabled());
if (empty($cflink['enabled']))
unset($cflink['enabled']);
$cflink['default'] = trim($this->GetDefault());
if (empty($cflink['default']))
unset($cflink['default']);
$cflink['red'] = trim($this->GetRed());
if (empty($cflink['red']))
unset($cflink['red']);
$cflink['rio'] = trim($this->GetRio());
if (empty($cflink['rio']))
unset($cflink['rio']);
$cflink['ecn'] = trim($this->GetEcn());
if (empty($cflink['ecn']))
unset($cflink['ecn']);
$cflink['borrow'] = trim($this->GetBorrow());
if (empty($cflink['borrow']))
unset($cflink['borrow']);
if (is_array($this->queues)) {
$cflinkp['queue'] = array();
foreach ($this->subqueues as $q) {
$cflink['queue'][$q->GetQname()] = array();
$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
}
}
}
/*
* Should search even its children
*/
function &find_queue($interface, $qname) {
if ($qname == $this->GetQname())
return $this;
foreach ($this->subqueues as $q) {
$result =& $q->find_queue("", $qname);
if ($result)
return $result;
}
}
function &find_parentqueue($interface, $qname) {
if ($this->subqueues[$qname])
return $this;
foreach ($this->subqueues as $q) {
$result = $q->find_parentqueue("", $qname);
if ($result)
return $result;
}
}
function delete_queue() {
unref_on_altq_queue_list($this->GetQname());
cleanup_queue_from_rules($this->GetQname());
foreach ($this->subqueues as $q) {
$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
$q->delete_queue();
}
unset_object_by_reference($this->GetLink());
}
function validate_input($data, &$input_errors) {
parent::validate_input($data, $input_errors);
if ($data['priority'] > 7)
$input_errors[] = gettext("Priority must be an integer between 1 and 7.");
$reqdfields[] = "bandwidth";
$reqdfieldsn[] = gettext("Bandwidth");
$reqdfields[] = "bandwidthtype";
$reqdfieldsn[] = gettext("Bandwidthtype");
shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
if ($data['bandwidth'] && !is_numeric($data['bandwidth']))
$input_errors[] = gettext("Bandwidth must be an integer.");
if ($data['bandwidth'] < 0)
$input_errors[] = gettext("Bandwidth cannot be negative.");
if ($data['bandwidthtype'] == "%") {
if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0)
$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
}
/*
$parent =& $this->GetParent();
switch ($data['bandwidthtype']) {
case "%":
$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
break;
default:
$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
break;
}
if ($parent->GetAvailableBandwidth() < floatval($myBw))
$input_errors[] = "The sum of the children bandwidth exceeds that of the parent.";
*/
}
function ReadConfig(&$q) {
parent::ReadConfig($q);
if (!empty($q['borrow']))
$this->SetBorrow("on");
else
$this->SetBorrow("");
}
function build_javascript() {
return parent::build_javascript();
}
function build_tree() {
$tree = "
";
if (is_array($bandwidth)) {
foreach ($bandwidth as $bwidx => $bw) {
$form .= "\n
";
$form .= "";
//$form .= "
";
//$form .= "";
$form .= "
";
$form .= "";
$form .= "
";
$form .= "";
$form .= "
";
}
}
$form .= "
";
$form .= "";
$form .= "";
//$form .= " " . gettext("Bandwidth is a rate (e.g. Mbit/s), burst is a total amount of data that will be transferred at full speed after an idle period.") . " ";
$form .= " " . gettext("Bandwidth is the rate (e.g. Mbit/s) to which traffic in this limiter will be restricted.") . " ";
$form .= "
";
$form .= "
" . gettext("Mask") . "
";
$form .= "
";
$form .= "";
$form .= " ";
$form .= "" . gettext("If 'source' or 'destination' slots is chosen, \n"
. "a dynamic pipe with the bandwidth, delay, packet loss and queue size given above will \n"
. "be created for each source/destination IP address encountered, \n"
. "respectively. This makes it possible to easily specify bandwidth \n"
. "limits per host.") . " ";
$form .= "255.255.255.255/ "none")
$form .= $mask['bits'];
$form .= "\"";
if ($mask['type'] == "none")
$form .= " disabled";
$form .= " />";
$form .= " IPV4 mask bits (1-32) ";
$form .= "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/ "none")
$form .= $mask['bitsv6'];
$form .= "\"";
if ($mask['type'] == "none")
$form .= " disabled";
$form .= " />";
$form .= " IPV6 mask bits (1-128) ";
$form .= "" . gettext("If 'source' or 'destination' slots is chosen, \n"
. "leaving the mask bits blank will create one pipe per host. Otherwise specify \n"
. "the number of 'one' bits in the subnet mask used to group multiple hosts \n"
. "per pipe.") . "";
$form .= "
";
$form .= "
" . gettext("Description") . "
";
$form .= "
";
$form .= "GetDescription();
$form .= "\" />";
$form .= " ";
$form .= gettext("You may enter a description here for your reference (not parsed).") . "";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Delay") . "
";
$form .= "
";
$form .= "GetDelay() . "\" />";
$form .= " ms " . gettext("Hint: in most cases, you "
. "should specify 0 here (or leave the field empty)") . " ";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Packet loss rate") . "
";
$form .= "
";
$form .= "GetPlr() . "\" />";
$form .= " " . gettext("Hint: in most cases, you "
. "should specify 0 here (or leave the field empty). "
. "A value of 0.001 means one packet in 1000 gets dropped") . "";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Queue Size") . "
";
$form .= "
";
$form .= "GetQlimit() . "\" />";
$form .= " slots ";
$form .= "" . gettext("Hint: in most cases, you "
. "should leave the field 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.") . "";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Bucket Size") . "
";
$form .= "
";
$form .= "GetBuckets() . "\" />";
$form .= " slots ";
$form .= "" . gettext("Hint: in most cases, you "
. "should leave the field empty. It increases the hash size set.");
$form .= "
";
return $form;
}
function wconfig() {
$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
if (!is_array($cflink))
$cflink = array();
$cflink['name'] = $this->GetQname();
$cflink['number'] = $this->GetNumber();
$cflink['qlimit'] = $this->GetQlimit();
$cflink['plr'] = $this->GetPlr();
$cflink['description'] = $this->GetDescription();
$bandwidth = $this->GetBandwidth();
if (is_array($bandwidth)) {
$cflink['bandwidth'] = array();
$cflink['bandwidth']['item'] = array();
foreach ($bandwidth as $bwidx => $bw)
$cflink['bandwidth']['item'][] = $bw;
}
$cflink['enabled'] = $this->GetEnabled();
$cflink['buckets'] = $this->GetBuckets();
$mask = $this->GetMask();
$cflink['mask'] = $mask['type'];
$cflink['maskbits'] = $mask['bits'];
$cflink['maskbitsv6'] = $mask['bitsv6'];
$cflink['delay'] = $this->GetDelay();
}
}
class dnqueue_class extends dummynet_class {
var $pipeparent;
var $weight;
function GetWeight() {
return $this->weight;
}
function SetWeight($weight) {
$this->weight = $weight;
}
function GetPipe() {
return $this->pipeparent;
}
function SetPipe($pipe) {
$this->pipeparent = $pipe;
}
/* Just a stub in case we ever try to call this from the frontend. */
function &add_queue($interface, &$queue, &$path, &$input_errors) { return; }
function delete_queue() {
cleanup_dnqueue_from_rules($this->GetQname());
unset_dn_object_by_reference($this->GetLink());
@pfSense_pipe_action("queue delete " . $this->GetNumber());
}
function validate_input($data, &$input_errors) {
parent::validate_input($data, $input_errors);
if ($data['weight'] && ((!is_numeric($data['weight'])) ||
($data['weight'] < 1 && $data['weight'] > 100)))
$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
}
/*
* Should search even its children
*/
function &find_queue($pipe, $qname) {
if ($qname == $this->GetQname())
return $this;
else
return NULL;
}
function &find_parentqueue($pipe, $qname) {
return $this->qparent;
}
function &get_queue_list(&$qlist) {
if ($this->GetEnabled() == "")
return;
$qlist[$this->GetQname()] = "?" .$this->GetNumber();
}
function ReadConfig(&$q) {
if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
$this->SetQname($q['newname']);
} else if (!empty($q['newname'])) {
$this->SetQname($q['newname']);
} else {
$this->SetQname($q['name']);
}
$this->SetNumber($q['number']);
if (isset($q['qlimit']) && $q['qlimit'] <> "")
$this->SetQlimit($q['qlimit']);
else
$this->SetQlimit("");
if (isset($q['mask']) && $q['mask'] <> "")
$masktype = $q['mask'];
else
$masktype = "";
if (isset($q['maskbits']) && $q['maskbits'] <> "")
$maskbits = $q['maskbits'];
else
$maskbits = "";
if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "")
$maskbitsv6 = $q['maskbitsv6'];
else
$maskbitsv6 = "";
$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
if (isset($q['buckets']) && $q['buckets'] <> "")
$this->SetBuckets($q['buckets']);
else
$this->SetBuckets("");
if (isset($q['plr']) && $q['plr'] <> "")
$this->SetPlr($q['plr']);
else
$this->SetPlr("");
if (isset($q['weight']) && $q['weight'] <> "")
$this->SetWeight($q['weight']);
else
$this->SetWeight("");
if (isset($q['description']) && $q['description'] <> "")
$this->SetDescription($q['description']);
else
$this->SetDescription("");
$this->SetEnabled($q['enabled']);
}
function build_tree() {
$parent =& $this->GetParent();
$tree = "
";
$form .= "";
$form .= " slots ";
$form .= "" . gettext("If 'source' or 'destination' slots is chosen, \n"
. "a dynamic pipe with the bandwidth, delay, packet loss and queue size given above will \n"
. "be created for each source/destination IP address encountered, \n"
. "respectively. This makes it possible to easily specify bandwidth \n"
. "limits per host.") . " ";
$form .= "255.255.255.255/ "none")
$form .= $mask['bits'];
$form .= "\"";
if ($mask['type'] == "none")
$form .= " disabled";
$form .= " />";
$form .= " IPV4 mask bits (1-32) ";
$form .= "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/ "none")
$form .= $mask['bitsv6'];
$form .= "\"";
if ($mask['type'] == "none")
$form .= " disabled";
$form .= " />";
$form .= " IPV6 mask bits (1-128) ";
$form .= "" . gettext("If 'source' or 'destination' slots is chosen, \n"
. "leaving the mask bits blank will create one pipe per host. Otherwise specify \n"
. "the number of 'one' bits in the subnet mask used to group multiple hosts \n"
. "per queue.") . "";
$form .= "
";
$form .= "
" . gettext("Description") . "
";
$form .= "
";
$form .= "GetDescription();
$form .= "\" />";
$form .= " ";
$form .= gettext("You may enter a description here for your reference (not parsed).") . "";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Weight") . "
";
$form .= "
";
$form .= "GetWeight() . "\" />";
$form .= " " . gettext("Hint: For queues under the same parent "
. "this specifies the share that a queue gets(values range from 1 to 100, you can leave it blank otherwise)") . "";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Packet loss rate") . "
";
$form .= "
";
$form .= "GetPlr() . "\" />";
$form .= " " . gettext("Hint: in most cases, you "
. "should specify 0 here (or leave the field empty). "
. "A value of 0.001 means one packet in 1000 gets dropped") . "";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Queue Size") . "
";
$form .= "
";
$form .= "GetQlimit() . "\" />";
$form .= " slots ";
$form .= "" . gettext("Hint: in most cases, you "
. "should leave the field 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.") . "";
$form .= "
";
$form .= "
";
$form .= "
" . gettext("Bucket Size") . "
";
$form .= "
";
$form .= "GetBuckets() . "\" />";
$form .= " " . gettext("slots") . " ";
$form .= "" . gettext("Hint: in most cases, you "
. "should leave the field empty. It increases the hash size set.");
$form .= "
";
$form .= "GetPipe() . "\" />";
return $form;
}
function update_dn_data(&$data) {
$this->ReadConfig($data);
}
function wconfig() {
$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
if (!is_array($cflink))
$cflink = array();
$cflink['name'] = $this->GetQname();
$cflink['number'] = $this->GetNumber();
$cflink['qlimit'] = $this->GetQlimit();
$cflink['description'] = $this->GetDescription();
$cflink['weight'] = $this->GetWeight();
$cflink['enabled'] = $this->GetEnabled();
$cflink['buckets'] = $this->GetBuckets();
$mask = $this->GetMask();
$cflink['mask'] = $mask['type'];
$cflink['maskbits'] = $mask['bits'];
$cflink['maskbitsv6'] = $mask['bitsv6'];
}
}
// List of layer7 objects
$layer7_rules_list = array();
class layer7 {
var $rname; //alias
var $rdescription; //alias description
var $rport; //divert port
var $renabled; //rule enabled
var $rsets = array(); //array of l7 associations
// Auxiliary functions
function GetRName() {
return $this->rname;
}
function SetRName($rname) {
$this->rname = $rname;
}
function GetRDescription() {
return $this->rdescription;
}
function SetRDescription($rdescription) {
$this->rdescription = $rdescription;
}
function GetRPort() {
return $this->rport;
}
function SetRPort($rport) {
$this->rport = $rport;
}
function GetREnabled() {
return $this->renabled;
}
function SetREnabled($value) {
$this->renabled = $value;
}
function GetRl7() {
return $this->rsets;
}
function SetRl7($rsets) {
$this->rsets = $rsets;
}
//Add a tuple (rule,sctructure,element) to the $rsets
function add_rule($l7set) {
$this->rsets[] = $l7set;
}
// Build the layer7 rules
function build_l7_rules() {
if($this->GetREnabled() == "") {
return;
}
//$l7rules = "#" . $this->rdescription . "\n";
foreach ($this->rsets as $rl7) {
$l7rules .= $rl7->build_rules();
}
return $l7rules;
}
// Read the config from array
function ReadConfig(&$qname, &$q) {
$this->SetRName($qname);
$this->SetREnabled($q['enabled']);
$this->SetRPort($q['divert_port']);
if(isset($q['description']) && $q['description'] <> "")
$this->SetRDescription($q['description']);
$rsets = $q['l7rules'];
//Put individual rules in the array
if(is_array($rsets)) {
$this->rsets = array(); // XXX: ugly hack
foreach($rsets as $l7r) {
$l7obj = new l7rule();
$l7obj->SetRProtocol($l7r['protocol']);
$l7obj->SetRStructure($l7r['structure']);
$l7obj->SetRBehaviour($l7r['behaviour']);
$this->add_rule($l7obj);
}
}
}
//Generate a random port for the divert socket
function gen_divert_port() {
$dports = get_divert_ports(); //array of used ports
$divert_port = 1; // Initialize
while (($divert_port % 2) != 0 || in_array($divert_port, $dports)) {
$divert_port = rand(40000, 60000);
}
return $divert_port;
}
//Helps building the left tree
function build_tree() {
$tree = "
";
$form .= "GetRDescription();
$form .= "\" />";
$form .= " ";
$form .= gettext("You may enter a description here for your reference (not parsed).") . "";
$form .= "
";
return $form;
}
//Write the setting to the $config array
function wconfig() {
global $config;
if(!is_array($config['l7shaper']['container'])) {
$config['l7shaper']['container'] = array();
}
//
$cflink =& get_l7c_reference_to_me_in_config($this->GetRName());
// Test if this rule does exists already
if(!$cflink) {
$cflink =& $config['l7shaper']['container'][];
}
$cflink['name'] = $this->GetRName();
$cflink['enabled'] = $this->GetREnabled();
$cflink['description'] = $this->GetRDescription();
$cflink['divert_port'] = $this->GetRPort();
//Destroy previously existent rules
if(is_array($cflink['rules'])) {
unset($cflink['l7rules']);
}
$cflink['l7rules'] = array();
$i = 0;
foreach($this->rsets as $rulel7) {
$cflink['l7rules'][$i]['protocol'] = $rulel7->GetRProtocol();
$cflink['l7rules'][$i]['structure'] = $rulel7->GetRStructure();
$cflink['l7rules'][$i]['behaviour'] = $rulel7->GetRBehaviour();
$i++;
}
}
//This function is necessary to help producing the overload options for keep state
function get_unique_structures() {
$unique_structures = array("action" => false, "dummynet" => false, "altq" => false);
foreach($this->rsets as $l7rule) {
if($l7rule->GetRStructure() == "action")
$unique_structures['action'] = true;
else if($l7rule->GetRStructure() == "limiter")
$unique_structures['dummynet'] = true;
else
$unique_structures['altq'] = true;
}
//Delete non used structures so we don't have to check this in filter.inc
foreach($unique_structures as $key => $value)
if(!$value)
unset($unique_structures[$key]);
return $unique_structures;
}
function validate_input($data, &$input_errors) {
$reqdfields[] = "container";
$reqdfieldsn[] = gettext("Name");
shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
if (!preg_match("/^[a-zA-Z0-9_-]+$/", $data['container']))
$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
}
function delete_l7c() {
mwexec("/bin/pkill -f 'ipfw-classifyd .* -p ". $this->GetRPort() . "'", true);
unset_l7_object_by_reference($this->GetRName());
cleanup_l7_from_rules($this->GetRName());
}
}
class l7rule {
var $rprotocol; //protocol
var $rstructure; //action, limiter, queue
var $rbehaviour; //allow, block, queue_name, pipe_number ...
//Auxiliary Functions
function GetRProtocol() {
return $this->rprotocol;
}
function SetRProtocol($rprotocol) {
$this->rprotocol = $rprotocol;
}
function GetRStructure() {
return $this->rstructure;
}
function SetRStructure($rstructure) {
$this->rstructure = $rstructure;
}
function GetRBehaviour() {
return $this->rbehaviour;
}
function SetRBehaviour($rbehaviour) {
$this->rbehaviour = $rbehaviour;
}
//XXX Do we need to test any particularity for AltQ queues?
function build_rules() {
global $dummynet_pipe_list;
switch ($this->GetRStructure()) {
case "limiter":
read_dummynet_config();
$dn_list =& get_unique_dnqueue_list();
$found = false;
if(is_array($dn_list)) {
foreach($dn_list as $key => $value) {
if($key == $this->GetRBehaviour()) {
if($value[0] == "?")
$l7rule = $this->GetRProtocol() . " = dnqueue " . substr($value, 1) . "\n";
else
$l7rule = $this->GetRProtocol() . " = dnpipe " . $value . "\n";
$found = true;
}
if($found)
break;
}
}
break;
default: //This is for action and for altq
$l7rule = $this->GetRProtocol() . " = " . $this->GetRStructure() . " " . $this->GetRBehaviour() . "\n";
break;
}
return $l7rule;
}
}
/*
* This function allows to return an array with all the used divert socket ports
*/
function get_divert_ports() {
global $layer7_rules_list;
$dports = array();
foreach($layer7_rules_list as $l7r)
$dports[] = $l7r->GetRPort();
return $dports;
}
function &get_l7c_reference_to_me_in_config(&$name) {
global $config;
$ptr = NULL;
if(is_array($config['l7shaper']['container'])) {
foreach($config['l7shaper']['container'] as $key => $value) {
if($value['name'] == $name)
$ptr =& $config['l7shaper']['container'][$key];
}
}
return $ptr;
// $ptr can be null. has to be checked later
}
function unset_l7_object_by_reference(&$name) {
global $config;
if(is_array($config['l7shaper']['container'])) {
foreach($config['l7shaper']['container'] as $key => $value) {
if($value['name'] == $name) {
unset($config['l7shaper']['container'][$key]['l7rules']);
unset($config['l7shaper']['container'][$key]);
break;
}
}
}
}
function read_layer7_config() {
global $layer7_rules_list, $config;
if (!is_array($config['l7shaper']['container']) || !count($config['l7shaper']['container'])) {
$layer7_rules_list = array();
return;
}
$l7cs = &$config['l7shaper']['container'];
$layer7_rules_list = array();
foreach ($l7cs as $conf) {
if (empty($conf['name']))
continue; /* XXX: grrrrrr at php */
$root =& new layer7();
$root->ReadConfig($conf['name'],$conf);
$layer7_rules_list[$root->GetRName()] = &$root;
}
}
function update_layer7_custom_patterns() {
global $config;
if (!is_array($config['l7shaper']['custom_pat']))
return;
foreach ($config['l7shaper']['custom_pat'] as $filename => $filecontent)
if (!file_exists("/usr/local/share/protocols/" . $filename))
@file_put_contents("/usr/local/share/protocols/" . $filename, base64_decode($filecontent));
}
function generate_layer7_files() {
global $layer7_rules_list, $g;
read_layer7_config();
if (!empty($layer7_rules_list)) {
if (!is_module_loaded("ipdivert.ko"))
mwexec("/sbin/kldload ipdivert.ko");
array_map('unlink', glob("{$g['tmp_path']}/*.l7"));
}
update_layer7_custom_patterns();
foreach($layer7_rules_list as $l7rules) {
if($l7rules->GetREnabled()) {
$filename = $l7rules->GetRName() . ".l7";
$path = "{$g['tmp_path']}/" . $filename;
$rules = $l7rules->build_l7_rules();
$fp = fopen($path,'w');
fwrite($fp,$rules);
fclose($fp);
}
}
}
function layer7_start_l7daemon() {
global $layer7_rules_list, $g;
/*
* XXX: ermal - Needed ?!
* read_layer7_config();
*/
foreach($layer7_rules_list as $l7rules) {
if($l7rules->GetREnabled()) {
$filename = $l7rules->GetRName() . ".l7";
$path = "{$g['tmp_path']}/" . $filename;
unset($l7pid);
/* Only reread the configuration rather than restart to avoid losing information. */
exec("/bin/pgrep -f 'ipfw-classifyd .* -p ". $l7rules->GetRPort() . "'", $l7pid);
if (count($l7pid) > 0) {
log_error(sprintf(gettext("Sending HUP signal to %s"), $l7pid[0]));
mwexec("/bin/kill -HUP {$l7pid[0]}");
} else {
// XXX: Hardcoded number of packets to garbage collect and queue length..
$ipfw_classifyd_init = "/usr/local/sbin/ipfw-classifyd -n 8 -q 700 -c {$path} -p " . $l7rules->GetRPort() . " -P /usr/local/share/protocols";
mwexec_bg($ipfw_classifyd_init);
}
}
}
}
// This function uses /usr/local/share/protocols as a default directory for searching .pat files
function generate_protocols_array() {
update_layer7_custom_patterns();
$protocols = return_dir_as_array("/usr/local/share/protocols");
$protocols_new = array();
if(is_array($protocols)) {
foreach($protocols as $key => $proto) {
if (strstr($proto, ".pat"))
$protocols_new[$key] =& str_replace(".pat", "", $proto);
}
sort($protocols_new);
}
return $protocols_new;
}
function get_l7_unique_list() {
global $layer7_rules_list;
$l7list = array();
if(is_array($layer7_rules_list))
foreach($layer7_rules_list as $l7c)
if($l7c->GetREnabled())
$l7list[] = $l7c->GetRName();
return $l7list;
}
// Disable a removed l7 container from the filter
function cleanup_l7_from_rules(&$name) {
global $config;
if(is_array($config['filter']['rule']))
foreach ($config['filter']['rule'] as $key => $rule) {
if ($rule['l7container'] == $name)
unset($config['filter']['rule'][$key]['l7container']);
}
}
function get_dummynet_name_list() {
$dn_name_list =& get_unique_dnqueue_list();
$dn_name = array();
if(is_array($dn_name_list))
foreach($dn_name_list as $key => $value)
$dn_name[] = $key;
return $dn_name;
}
function get_altq_name_list() {
$altq_name_list =& get_unique_queue_list();
$altq_name = array();
if(is_array($altq_name_list))
foreach($altq_name_list as $key => $aqobj)
$altq_name[] = $key;
return $altq_name;
}
/*
* XXX: TODO Make a class shaper to hide all these function
* from the global namespace.
*/
/*
* This is a layer violation but for now there is no way
* i can find to properly do this with PHP.
*/
function altq_get_default_queue($interface) {
global $altq_list_queues;
$altq_tmp = $altq_list_queues[$interface];
if ($altq_tmp)
return $altq_tmp->GetDefaultQueuePresent();
else
return false;
}
function altq_check_default_queues() {
global $altq_list_queues;
$count = 0;
if (is_array($altq_list_queues)) {
foreach($altq_list_queues as $altq) {
if ($altq->GetDefaultQueuePresent())
$count++;
}
}
else $count++;
return 0;
}
function &get_unique_queue_list() {
global $altq_list_queues;
$qlist = array();
if (is_array($altq_list_queues)) {
foreach ($altq_list_queues as $altq) {
if ($altq->GetEnabled() == "")
continue;
$tmplist =& $altq->get_queue_list();
foreach ($tmplist as $qname => $link) {
if ($link->GetEnabled() <> "")
$qlist[$qname] = $link;
}
}
}
return $qlist;
}
function &get_unique_dnqueue_list() {
global $dummynet_pipe_list;
$qlist = array();
if (is_array($dummynet_pipe_list)) {
foreach ($dummynet_pipe_list as $dn) {
if ($dn->GetEnabled() == "")
continue;
$tmplist =& $dn->get_queue_list();
foreach ($tmplist as $qname => $link) {
$qlist[$qname] = $link;
}
}
}
return $qlist;
}
function ref_on_altq_queue_list($parent, $qname) {
if (isset($GLOBALS['queue_list'][$qname]))
$GLOBALS['queue_list'][$qname]++;
else
$GLOBALS['queue_list'][$qname] = 1;
unref_on_altq_queue_list($parent);
}
function unref_on_altq_queue_list($qname) {
$GLOBALS['queue_list'][$qname]--;
if ($GLOBALS['queue_list'][$qname] <= 1)
unset($GLOBALS['queue_list'][$qname]);
}
function read_altq_config() {
global $altq_list_queues, $config;
$path = array();
if (!is_array($config['shaper']))
$config['shaper'] = array();
if (!is_array($config['shaper']['queue']))
$config['shaper']['queue'] = array();
$a_int = &$config['shaper']['queue'];
$altq_list_queues = array();
if (!is_array($config['shaper']['queue']))
return;
foreach ($a_int as $key => $conf) {
$int = $conf['interface'];
$root =& new altq_root_queue();
$root->SetInterface($int);
$altq_list_queues[$root->GetInterface()] = &$root;
$root->ReadConfig($conf);
array_push($path, $key);
$root->SetLink($path);
if (is_array($conf['queue'])) {
foreach ($conf['queue'] as $key1 => $q) {
array_push($path, $key1);
/*
* XXX: we completely ignore errors here but anyway we must have
* checked them before so no harm should be come from this.
*/
$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
array_pop($path);
}
}
array_pop($path);
}
}
function read_dummynet_config() {
global $dummynet_pipe_list, $config;
$path = array();
if (!is_array($config['dnshaper']))
$config['dnshaper'] = array();
if (!is_array($config['dnshaper']['queue']))
$config['dnshaper']['queue'] = array();
$a_int = &$config['dnshaper']['queue'];
$dummynet_pipe_list = array();
if (!is_array($config['dnshaper']['queue'])
|| !count($config['dnshaper']['queue']))
return;
foreach ($a_int as $key => $conf) {
if (empty($conf['name']))
continue; /* XXX: grrrrrr at php */
$root =& new dnpipe_class();
$root->ReadConfig($conf);
$dummynet_pipe_list[$root->GetQname()] = &$root;
array_push($path, $key);
$root->SetLink($path);
if (is_array($conf['queue'])) {
foreach ($conf['queue'] as $key1 => $q) {
array_push($path, $key1);
/*
* XXX: we completely ignore errors here but anyway we must have
* checked them before so no harm should be come from this.
*/
$root->add_queue($root->GetQname(), $q, $path, $input_errors);
array_pop($path);
}
}
array_pop($path);
}
}
function get_interface_list_to_show() {
global $altq_list_queues, $config;
global $shaperIFlist;
$tree = "";
foreach ($shaperIFlist as $shif => $shDescr) {
if ($altq_list_queues[$shif]) {
continue;
} else {
if (!is_altq_capable(get_real_interface($shif)))
continue;
$tree .= "
";
$default_shaper_msg .= "" . sprintf(gettext("Welcome to the %s Traffic Shaper."), $g['product_name']) . " ";
$default_shaper_msg .= gettext("The tree on the left helps you navigate through the queues "
. "buttons at the bottom represent queue actions and are activated accordingly.");
$default_shaper_msg .= "";
$default_shaper_msg .= "
";
$dn_default_shaper_msg = "
";
$dn_default_shaper_msg .= "" . sprintf(gettext("Welcome to the %s Traffic Shaper."), $g['product_name']) . " ";
$dn_default_shaper_msg .= gettext("The tree on the left helps you navigate through the queues "
. "buttons at the bottom represent queue actions and are activated accordingly.");
$dn_default_shaper_msg .= "";
$dn_default_shaper_msg .= "