GetInterface(); $altq =& $altq_list_queues[$int]; if ($altq) { $bw_3 = $altq->GetBandwidth(); $bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale()); return floatval($bw_3); } else { return 0; } } /* * This is duplicated here since we cannot include guiconfig.inc. * Including it makes all stuff break. */ function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) { /* check for bad control characters */ foreach ($postdata as $pn => $pd) { if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) { $input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn); } } for ($i = 0; $i < count($reqdfields); $i++) { if ($postdata[$reqdfields[$i]] == "") { $input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]); } } } function cleanup_queue_from_rules($queue) { global $config; foreach ($config['filter']['rule'] as $rule) { if ($rule['defaultqueue'] == $queue) { unset($rule['defaultqueue']); } if ($rule['ackqueue'] == $queue) { unset($rule['ackqueue']); } } } function cleanup_dnqueue_from_rules($queue) { global $config; foreach ($config['filter']['rule'] as $rule) { if ($rule['dnpipe'] == $queue) { unset($rule['dnpipe']); } if ($rule['pdnpipe'] == $queue) { unset($rule['pdnpipe']); } } } class altq_root_queue { var $interface; var $tbrconfig ; var $bandwidth; var $bandwidthtype; /* b, Kb, Mb */ var $scheduler; var $qlimit; var $queues = array(); var $qenabled = false; var $link; var $available_bw; /* in b/s */ /* Accessor functions */ function GetAvailableBandwidth() { return $this->available_bw; } function SetAvailableBandwidth($bw) { $this->available_bw = $bw; } function GetDefaultQueuePresent() { if (!empty($this->queues)) { foreach ($this->queues as $q) { if ($q->GetDefault()) { return true; } } } return false; } function SetLink($link) { $this->link = $link; } function GetLink() { return $this->link; } function GetEnabled() { return $this->qenabled; } function SetEnabled($value) { $this->qenabled = $value; } function CanHaveChildren() { if ($this->GetScheduler() == "CODELQ") { return false; } else { return true; } } function CanBeDeleted() { return false; } function GetQname() { return $this->interface; } function SetQname($name) { $this->interface = trim($name); } function GetInterface() { return $this->interface; } function SetInterface($name) { $this->interface = trim($name); } function GetTbrConfig() { return $this->tbrconfig; } function SetTbrConfig($tbrconfig) { $this->tbrconfig = $tbrconfig; } function GetBandwidth() { return $this->bandwidth; } function SetBandwidth($bw) { $this->bandwidth = $bw; } function GetBwscale() { return $this->bandwidthtype; } function SetBwscale($bwscale) { $this->bandwidthtype = $bwscale; } function GetScheduler() { return $this->scheduler; } function SetScheduler($scheduler) { $this->scheduler = trim($scheduler); } function GetQlimit() { return $this->qlimit; } function SetQlimit($limit) { $this->qlimit = $limit; } function validate_input($data, &$input_errors) { $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['qlimit'] && (!is_numeric($data['qlimit']))) { $input_errors[] = gettext("Qlimit must be an integer."); } if ($data['qlimit'] < 0) { $input_errors[] = gettext("Qlimit must be positive."); } if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) { $input_errors[] = gettext("Tbrsize must be an integer."); } if ($data['tbrconfig'] < 0) { $input_errors[] = gettext("Tbrsize must be positive."); } } /* Implement this to shorten some code on the frontend page */ function ReadConfig(&$conf) { if (isset($conf['tbrconfig'])) { $this->SetTbrConfig($conf['tbrconfig']); } else { $this->SetTbrConfig($conf['tbrconfig']); } $this->SetBandwidth($conf['bandwidth']); if ($conf['bandwidthtype'] <> "") { $this->SetBwscale($conf['bandwidthtype']); } if (isset($conf['scheduler'])) { if ($this->GetScheduler() != $conf['scheduler']) { foreach ($this->queues as $q) { clean_child_queues($conf['scheduler'], $this->GetLink()); $q->clean_queue($conf['scheduler']); } } $this->SetScheduler($conf['scheduler']); } if (isset($conf['qlimit']) && $conf['qlimit'] <> "") { $this->SetQlimit($conf['qlimit']); } else { $this->SetQlimit(""); } if (isset($conf['name'])) { $this->SetQname($conf['name']); } if (!empty($conf['enabled'])) { $this->SetEnabled($conf['enabled']); } else { $this->SetEnabled(""); } } function copy_queue($interface, &$cflink) { $cflink['interface'] = $interface; $cflink['name'] = $interface; $cflink['scheduler'] = $this->GetScheduler(); $cflink['bandwidth'] = $this->GetBandwidth(); $cflink['bandwidthtype'] = $this->GetBwscale(); $cflink['qlimit'] = $this->GetQlimit(); $cflink['tbrconfig'] = $this->GetTbrConfig(); $cflink['enabled'] = $this->GetEnabled(); if (is_array($this->queues)) { $cflink['queue'] = array(); foreach ($this->queues as $q) { $cflink['queue'][$q->GetQname()] = array(); $q->copy_queue($interface, $cflink['queue'][$q->GetQname()]); } } } function &get_queue_list(&$q = null) { $qlist = array(); //$qlist[$this->GetQname()] = & $this; if (is_array($this->queues)) { foreach ($this->queues as $queue) { $queue->get_queue_list($qlist); } } return $qlist; } function &add_queue($interface, &$queue, &$path, &$input_errors) { if (!is_array($this->queues)) { $this->queues = array(); } switch ($this->GetScheduler()) { case "PRIQ": $q =& new priq_queue(); break; case "HFSC": $q =& new hfsc_queue(); break; case "CBQ": $q =& new cbq_queue(); break; case "FAIRQ": $q =& new fairq_queue(); break; default: /* XXX: but should not happen anyway */ return; break; } $q->SetLink($path); $q->SetInterface($this->GetInterface()); $q->SetEnabled("on"); $q->SetParent($this); $q->ReadConfig($queue); $q->validate_input($queue, $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; } if (isset($queue['bandwidth'])) { switch ($queue['bandwidthtype']) { case "%": $myBw = $this->GetAvailableBandwidth() * $queue['bandwidth'] / 100; break; default: $myBw = $queue['bandwidth'] * get_bandwidthtype_scale($queue['bandwidthtype']); break; } } $q->SetAvailableBandwidth($myBw); $this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw); $this->queues[$q->GetQname()] = &$q; ref_on_altq_queue_list($this->GetQname(), $q->GetQname()); if (is_array($queue['queue'])) { foreach ($queue['queue'] as $key1 => $que) { array_push($path, $key1); $q->add_queue($q->GetInterface(), $que, $path, $input_errors); array_pop($path); } } return $q; } /* interface here might be optional */ function &find_queue($interface, $qname) { if ($qname == $this->GetQname()) { return $this; } foreach ($this->queues as $q) { $result =& $q->find_queue("", $qname); if ($result) { return $result; } } } function &find_parentqueue($interface, $qname) { if ($qname == $interface) { $result = NULL; } else if ($this->queues[$qname]) { $result = $this; } else if ($this->GetScheduler() <> "PRIQ") { foreach ($this->queues as $q) { $result = $q->find_parentqueue("", $qname); if ($result) { return $result; } } } } function build_tree() { global $shaperIFlist; $tree = "
  • GetInterface()."&queue=". $this->GetInterface()."&action=show"; $tree .= "\">" . $shaperIFlist[$this->GetInterface()] . ""; if (is_array($this->queues)) { $tree .= ""; } $tree .= "
  • "; return $tree; } function delete_queue() { foreach ($this->queues as $q) { $this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth()); $q->delete_queue(); } unset_object_by_reference($this->GetLink()); } function delete_all() { if (count($this->queues)) { foreach ($this->queues as $q) { $q->delete_all(); unset_object_by_reference($q->GetLink()); unset($q); } unset($this->queues); } } /* * First it spits: * altq on $interface .............. * then it goes like * foreach ($queues as $qkey => $queue) { * this->queues[$qkey]->build_rule(); * } */ function build_rules(&$default = false) { if (count($this->queues) > 0 && $this->GetEnabled() == "on") { $default = false; $rules = " altq on " . get_real_interface($this->GetInterface()); if ($this->GetScheduler()) { $rules .= " ".strtolower($this->GetScheduler()); } if ($this->GetQlimit() > 0) { $rules .= " qlimit " . $this->GetQlimit() . " "; } if ($this->GetBandwidth()) { $rules .= " bandwidth ".trim($this->GetBandwidth()); if ($this->GetBwscale()) { $rules .= $this->GetBwscale(); } } if ($this->GetTbrConfig()) { $rules .= " tbrsize ".$this->GetTbrConfig(); } if (count($this->queues)) { $i = count($this->queues); $rules .= " queue { "; foreach ($this->queues as $qkey => $qnone) { if ($i > 1) { $i--; $rules .= " {$qkey}, "; } else { $rules .= " {$qkey} "; } } $rules .= " } \n"; foreach ($this->queues as $q) { $rules .= $q->build_rules($default); } } if ($default == false) { $error = "SHAPER: no default queue specified for interface ". $this->GetInterface() . ". The interface queue will be enforced as default."; file_notice("Shaper", $error, "Error occurred", ""); unset($error); return "\n"; } $frule .= $rules; } else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") { $rules = " altq on " . get_real_interface($this->GetInterface()); if ($this->GetScheduler()) { $rules .= " ".strtolower($this->GetScheduler()); } if ($this->GetQlimit() > 0) { $rules .= " ( qlimit " . $this->GetQlimit() . " ) "; } if ($this->GetBandwidth()) { $rules .= " bandwidth ".trim($this->GetBandwidth()); if ($this->GetBwscale()) { $rules .= $this->GetBwscale(); } } if ($this->GetTbrConfig()) { $rules .= " tbrsize ".$this->GetTbrConfig(); } $rules .= " queue"; } $rules .= " \n"; return $rules; } function build_javascript() { $javascript = ""; return $javascript; } function build_shortform() { global $g; $altq =& $this; if ($altq) { $scheduler = ": " . $altq->GetScheduler(); } $form = '
    '; $form .= '
    '; $form .= ' ' . $shaperIFlist[$this->GetInterface()] . ''; $form .= '
    '; $form .= '
    '; $form .= $scheduler; $form .= '
    '; $form .= '
    '; $form .= 'Bandwidth'; $form .= '
    '; $form .= '
    '; $form .= $this->GetBandwidth() . ' ' . $this->GetBwscale(); $form .= '
    '; $form .= '
    '; $form .= 'Disable'; $form .= '
    '; $form .= '
    '; $form .= ''; $form .= gettext("Disable shaper on interface") . ''; $form .= '
    '; $form .= '
    '; return $form; } /* * For requesting the parameters of the root queues * to the user like the traffic wizard does. */ function build_form() { $sform = new Form(new Form_Button( 'Submit', 'Save' )); $section = new Form_Section(null); $section->addInput(new Form_Checkbox( 'enabled', 'Enable/Disable', 'Enable/disable discipline and its children', ($this->GetEnabled() == "on"), 'on' )); $section->addInput(new Form_StaticText( 'Name', $this->GetQname() )); $section->addInput(new Form_Select( 'scheduler', 'Scheduler Type', $this->GetScheduler(), array('HFSC' => 'HFSC', 'CBQ' => 'CBQ', 'FAIRQ' => 'FAIRQ', 'CODELQ' => 'CODELQ', 'PRIQ' => 'PRIQ') ))->setHelp('Changing this changes all child queues! Beware you can lose information.'); $group = new Form_group('Bandwidth'); $group->add(new Form_Input( 'bandwidth', null, 'number', $this->GetBandwidth() )); $group->add(new Form_Select( 'bandwidthtype', null, $this->GetBwscale(), array('Kb' => 'Kb', 'Mb' => 'Mb', 'Gb' => 'Gb', 'b' => 'b', '%' => '%') )); $section->add($group); $section->addInput(new Form_Input( 'qlimit', 'Queue Limit', 'number', $this->GetQlimit() )); $section->addInput(new Form_Input( 'tbrconfig', 'TRB Size', 'number', $this->GetTbrConfig() ))->setHelp('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.'); $section->addInput(new Form_Input( 'interface', null, 'hidden', $this->GetInterface() )); $section->addInput(new Form_Input( 'name', null, 'hidden', $this->GetQname() )); $sform->add($section); return($sform); } 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(); /* Accessor 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 = "
  • GetInterface()."&queue=". $this->GetQname()."&action=show"; $tree .= "\" "; $tmpvalue = $this->GetDefault(); if (!empty($tmpvalue)) { $tree .= " class=\"navlnk\""; } $tree .= " >" . $this->GetQname() . ""; /* * Not needed here! * if (is_array($queues) { * $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. (2) */ function build_form() { $sform = new Form(); $section = new Form_Section(null); $section->addInput(new Form_Checkbox( 'enabled', 'Enable/Disable', 'Enable/disable discipline and its children', ($this->GetEnabled() == "on"), 'on' )); $section->addInput(new Form_StaticText( 'Name', $this->GetQname() ))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.'); $section->addInput(new Form_Input( 'priority', 'Priority', 'number', $this->GetQpriority(), ['min' => '0', 'max'=> '7'] ))->setHelp('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.'); $section->addInput(new Form_Input( 'qlimit', 'Queue Limit', 'number', $this->GetQlimit() ))->setHelp('Queue limit in packets.'); $group = new Form_Group('Scheduler options'); if (empty($this->subqueues)) { $group->add(new Form_Checkbox( 'default', null, null, $this->GetDefault() ))->setHelp('Default Queue'); } $group->add(new Form_Checkbox( 'red', null, null, !empty($this->GetRed()) ))->setHelp('' . gettext('Random Early Detection') . ''); $group->add(new Form_Checkbox( 'rio', null, null, !empty($this->GetRio()) ))->setHelp('' . gettext('Random Early Detection In and Out') . ''); $group->add(new Form_Checkbox( 'ecn', null, null, !empty($this->GetEcn()) ))->setHelp('' . gettext('Explicit Congestion Notification') . ''); $group->add(new Form_Checkbox( 'codel', null, null, !empty($this->GetCodel()) ))->setHelp('' . gettext('Explicit Congestion Notification') . ''); $group->setHelp('Select options for this queue'); $section->add($group); $section->addInput(new Form_Input( 'description', 'Description', 'text', $this->GetDescription() )); $section->addInput(new Form_Input( 'interface', null, 'hidden', $this->GetInterface() )); $sform->add($section); return($sform); } function build_shortform() { /* XXX: Hacks in sight. Mostly layer violations! */ global $g, $altq_list_queues; global $shaperIFlist; $altq =& $altq_list_queues[$this->GetInterface()]; if ($altq) { $scheduler = $altq->GetScheduler(); } $form = '
    '; $form .= '
    '; $form .= ' ' . $shaperIFlist[$this->GetInterface()] . ''; $form .= '
    '; $form .= '
    '; $form .= $scheduler; $form .= '
    '; $form .= '
    '; $form .= 'Bandwidth'; $form .= '
    '; $form .= '
    '; $form .= $this->GetBandwidth() . ' ' . $this->GetBwscale(); $form .= '
    '; $tmpvalue = $this->GetQpriority(); if (!empty($tmpvalue)) { $form .= '
    '; $form .= 'Priority'; $form .= '
    '; $form .= '
    '; $form .= 'On'; $form .= '
    '; } $tmpvalue = $this->GetDefault(); if (!empty($tmpvalue)) { $form .= '
    '; $form .= 'Default'; $form .= '
    '; $form .= '
    '; $form .= 'On'; $form .= '
    '; } $form .= '
    '; $form .= 'Delete'; $form .= '
    '; $form .= '
    '; $form .= ''; $form .= gettext("Delete queue from interface") . ''; $form .= '
    '; $form .= '
    '; return $form; } function update_altq_queue_data(&$q) { $this->ReadConfig($q); } 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'] = trim($this->GetQpriority()); if (empty($cflink['priority'])) { unset($cflink['priority']); } $cflink['description'] = trim($this->GetDescription()); if (empty($cflink['description'])) { unset($cflink['description']); } $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['codel'] = trim($this->GetCodel()); if (empty($cflink['codel'])) { unset($cflink['codel']); } $cflink['rio'] = trim($this->GetRio()); if (empty($cflink['rio'])) { unset($cflink['rio']); } $cflink['ecn'] = trim($this->GetEcn()); if (empty($cflink['ecn'])) { unset($cflink['ecn']); } } } class hfsc_queue extends priq_queue { /* realtime */ var $realtime; var $r_m1; var $r_d; var $r_m2; /* linkshare */ var $linkshare; var $l_m1; var $l_d; var $l_m2; /* upperlimit */ var $upperlimit; var $u_m1; var $u_d; var $u_m2; /* * HFSC can have nested queues. */ function CanHaveChildren() { return true; } function GetRealtime() { return $this->realtime; } function GetR_m1() { return $this->r_m1; } function GetR_d() { return $this->r_d; } function GetR_m2() { return $this->r_m2; } function SetRealtime() { $this->realtime = "on"; } function DisableRealtime() { $this->realtime = ""; } function SetR_m1($value) { $this->r_m1 = $value; } function SetR_d($value) { $this->r_d = $value; } function SetR_m2($value) { $this->r_m2 = $value; } function GetLinkshare() { return $this->linkshare; } function DisableLinkshare() { $this->linkshare = ""; } function GetL_m1() { return $this->l_m1; } function GetL_d() { return $this->l_d; } function GetL_m2() { return $this->l_m2; } function SetLinkshare() { $this->linkshare = "on"; } function SetL_m1($value) { $this->l_m1 = $value; } function SetL_d($value) { $this->l_d = $value; } function SetL_m2($value) { $this->l_m2 = $value; } function GetUpperlimit() { return $this->upperlimit; } function GetU_m1() { return $this->u_m1; } function GetU_d() { return $this->u_d; } function GetU_m2() { return $this->u_m2; } function SetUpperlimit() { $this->upperlimit = "on"; } function DisableUpperlimit() { $this->upperlimit = ""; } function SetU_m1($value) { $this->u_m1 = $value; } function SetU_d($value) { $this->u_d = $value; } function SetU_m2($value) { $this->u_m2 = $value; } function &add_queue($interface, &$qname, &$path, &$input_errors) { if (!is_array($this->subqueues)) { $this->subqueues = array(); } $q =& new hfsc_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; } $q->SetEnabled("on"); $q->SetLink($path); 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); $this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue() 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['name'] = $this->GetQname(); $cflink['interface'] = $interface; $cflink['qlimit'] = trim($this->GetQlimit()); if (empty($cflink['qlimit'])) { unset($cflink['qlimit']); } $cflink['priority'] = trim($this->GetQpriority()); if (empty($cflink['priority'])) { unset($cflink['priority']); } $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']); } if ($this->GetLinkshare() <> "") { if ($this->GetL_m1() <> "") { $cflink['linkshare1'] = $this->GetL_m1(); $cflink['linkshare2'] = $this->GetL_d(); $cflink['linkshare'] = "on"; } else { unset($cflink['linkshare1']); unset($cflink['linkshare2']); unset($cflink['linkshare']); } if ($this->GetL_m2() <> "") { $cflink['linkshare3'] = $this->GetL_m2(); $cflink['linkshare'] = "on"; } else { unset($cflink['linkshare3']); unset($cflink['linkshare']); } } if ($this->GetRealtime() <> "") { if ($this->GetR_m1() <> "") { $cflink['realtime1'] = $this->GetR_m1(); $cflink['realtime2'] = $this->GetR_d(); $cflink['realtime'] = "on"; } else { unset($cflink['realtime1']); unset($cflink['realtime2']); unset($cflink['realtime']); } if ($this->GetR_m2() <> "") { $cflink['realtime3'] = $this->GetR_m2(); $cflink['realtime'] = "on"; } else { unset($cflink['realtime3']); unset($cflink['realtime']); } } 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['upperlimit3']); unset($cflink['upperlimit']); } } 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 delete_queue() { unref_on_altq_queue_list($this->GetQname()); cleanup_queue_from_rules($this->GetQname()); $parent =& $this->GetParent(); foreach ($this->subqueues as $q) { $this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth()); $q->delete_queue(); } unset_object_by_reference($this->GetLink()); } /* * 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 validate_input($data, &$input_errors) { parent::validate_input($data, $input_errors); $reqdfields[] = "bandwidth"; $reqdfieldsn[] = gettext("Bandwidth"); $reqdfields[] = "bandwidthtype"; $reqdfieldsn[] = gettext("Bandwidthtype"); shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); if (isset($data['linkshare3']) && $data['linkshare3'] <> "") { 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; default: $mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']); break; } if ($parent->GetAvailableBandwidth() < $myBw) { $input_errors[] = "The sum of children bandwidth exceeds that of the parent."; } */ } if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") { $input_errors[] = gettext("upperlimit service curve defined but missing (d) value"); } if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") { $input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value"); } if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) { $input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %"); } if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) { $input_errors[] = gettext("upperlimit d value needs to be numeric"); } if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) { $input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %"); } /* if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") { $bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']); $bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']); if (floatval($bw_1) < floatval($bw_2)) { $input_errors[] = ("upperlimit m1 cannot be smaller than m2"); } if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { $input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation."); } } */ if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") { $input_errors[] = gettext("linkshare service curve defined but missing (d) value"); } if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") { $input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value"); } if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) { $input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %"); } if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) { $input_errors[] = gettext("linkshare d value needs to be numeric"); } if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) { $input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %"); } if ($data['realtime1'] <> "" && $data['realtime2'] == "") { $input_errors[] = gettext("realtime service curve defined but missing (d) value"); } if ($data['realtime2'] <> "" && $data['realtime1'] == "") { $input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value"); } /* if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) { $bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']); $bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']); if (floatval($bw_1) < floatval($bw_2)) { $input_errors[] = ("linkshare m1 cannot be smaller than m2"); } if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { $input_errors[] = ("linkshare specification exceeds 80% of allowable allocation."); } } */ if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) { $input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %"); } if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) { $input_errors[] = gettext("realtime d value needs to be numeric"); } if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) { $input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %"); } /* if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) { $bw_1 = get_hfsc_bandwidth($this, $data['realtime1']); $bw_2 = get_hfsc_bandwidth($this, $data['realtime3']); if (floatval($bw_1) < floatval($bw_2)) { $input_errors[] = ("realtime m1 cannot be smaller than m2"); } if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) { $input_errors[] = ("realtime specification exceeds 80% of allowable allocation."); } } */ } function ReadConfig(&$cflink) { if (!empty($cflink['linkshare'])) { if (!empty($cflink['linkshare1'])) { $this->SetL_m1($cflink['linkshare1']); $this->SetL_d($cflink['linkshare2']); $this->SetLinkshare(); } else { $this->SetL_m1(""); $this->SetL_d(""); $this->DisableLinkshare(); } if (!empty($cflink['linkshare3'])) { $this->SetL_m2($cflink['linkshare3']); $this->SetLinkshare(); } } else { $this->DisableLinkshare(); } if (!empty($cflink['realtime'])) { if (!empty($cflink['realtime1'])) { $this->SetR_m1($cflink['realtime1']); $this->SetR_d($cflink['realtime2']); $this->SetRealtime(); } else { $this->SetR_m1(""); $this->SetR_d(""); $this->DisableRealtime(); } if (!empty($cflink['realtime3'])) { $this->SetR_m2($cflink['realtime3']); $this->SetRealtime(); } } else { $this->DisableRealtime(); } if (!empty($cflink['upperlimit'])) { if (!empty($cflink['upperlimit1'])) { $this->SetU_m1($cflink['upperlimit1']); $this->SetU_d($cflink['upperlimit2']); $this->SetUpperlimit(); } else { $this->SetU_m1(""); $this->SetU_d(""); $this->DisableUpperlimit(); } if (!empty($cflink['upperlimit3'])) { $this->SetU_m2($cflink['upperlimit3']); $this->SetUpperlimit(); } } else { $this->DisableUpperlimit(); } parent::ReadConfig($cflink); } function build_tree() { $tree = "
  • GetInterface() ."&queue=" . $this->GetQname()."&action=show"; $tree .= "\" "; $tmpvalue = $this->GetDefault(); if (!empty($tmpvalue)) { $tree .= " class=\"navlnk\""; } $tree .= " >" . $this->GetQname() . ""; if (is_array($this->subqueues)) { $tree .= ""; } $tree .= "
  • "; return $tree; } /* Even this should take children into consideration */ function build_rules(&$default = false) { $pfq_rule = " queue ". $this->qname; if ($this->GetInterface()) { $pfq_rule .= " on ".get_real_interface($this->GetInterface()); } if ($this->GetBandwidth() && $this->GetBwscale()) { $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); } $tmpvalue = $this->GetQlimit(); if (!empty($tmpvalue)) { $pfq_rule .= " qlimit " . $this->GetQlimit(); } if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") { $pfq_rule .= " hfsc ( "; $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 .= " ,"; } $comma = 1; $pfq_rule .= " default "; $default = true; } if ($this->GetRealtime() <> "") { if ($comma) { $pfq_rule .= " , "; } if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") { $pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") "; } else if ($this->GetR_m2() <> "") { $pfq_rule .= " realtime " . $this->GetR_m2(); } $comma = 1; } if ($this->GetLinkshare() <> "") { if ($comma) { $pfq_rule .= " ,"; } if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") { $pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") "; } else if ($this->GetL_m2() <> "") { $pfq_rule .= " linkshare " . $this->GetL_m2() . " "; } $comma = 1; } if ($this->GetUpperlimit() <> "") { if ($comma) { $pfq_rule .= " ,"; } if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") { $pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") "; } else if ($this->GetU_m2() <> "") { $pfq_rule .= " upperlimit " . $this->GetU_m2() . " "; } } $pfq_rule .= " ) "; } if (count($this->subqueues)) { $i = count($this->subqueues); $pfq_rule .= " { "; foreach ($this->subqueues as $qkey => $qnone) { if ($i > 1) { $i--; $pfq_rule .= " {$qkey}, "; } else { $pfq_rule .= " {$qkey} "; } } $pfq_rule .= " } \n"; foreach ($this->subqueues as $q) { $pfq_rule .= $q->build_rules($default); } } $pfq_rule .= " \n"; return $pfq_rule; } function build_javascript() { $javascript = << // EOJS; return $javascript; } function build_form() { $sform = parent::build_form(); $section = new Form_Section('Service Curve (sc)'); $group = new Form_Group('Bandwidth'); $group->add(new Form_Input( 'bandwidth', null, 'number', $this->GetBandwidth() )); $group->add(new Form_Select( 'bandwidthtype', null, $this->GetBwscale(), array('Kb' => 'Kb', 'Mb' => 'Mb', 'Gb' => 'Gb', 'b' => 'b', '%' => '%') )); $group->setHelp('Choose the amount of bandwidth for this queue'); $section->add($group); $group = new Form_Group('Max bandwidth for queue.'); $group->add(new Form_Checkbox( 'upperlimit', null, 'Upper Limit', ($this->GetUpperlimit()<> "") )); $group->add(new Form_Input( 'upperlimit1', null, 'text', $this->GetU_m1() ))->setHelp('m1'); $group->add(new Form_Input( 'upperlimit2', null, 'text', $this->GetU_d() ))->setHelp('d'); $group->add(new Form_Input( 'upperlimit3', null, 'text', $this->GetU_m2() ))->setHelp('m2'); $section->add($group); $group = new Form_Group('Min bandwidth for queue.'); $group->add(new Form_Checkbox( 'realtime', null, 'Real Time', ($this->GetRealtime()<> "") )); $group->add(new Form_Input( 'realtime1', null, 'text', $this->GetR_m1() ))->setHelp('m1'); $group->add(new Form_Input( 'realtime2', null, 'text', $this->GetR_d() ))->setHelp('d'); $group->add(new Form_Input( 'realtime3', null, 'text', $this->GetR_m2() ))->setHelp('m2'); $section->add($group); $group = new Form_Group('B/W share of a backlogged queue.'); $group->add(new Form_Checkbox( 'linkshare', null, 'Link Share', ($this->GetLinkshare()<> "") )); $group->add(new Form_Input( 'linkshare1', null, 'text', $this->GetL_m1() ))->setHelp('m1'); $group->add(new Form_Input( 'linkshare2', null, 'text', $this->GetL_d() ))->setHelp('d'); $group->add(new Form_Input( 'linkshare3', null, 'text', $this->GetL_m2() ))->setHelp('m2'); $group->sethelp('Bandwidth share overrides priority.' . '
    ' . '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.'); $section->add($group); $sform->add($section); return($sform); } 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 = "
  • GetInterface()."&queue=" . $this->GetQname()."&action=show"; $tree .= "\" "; $tmpvalue = trim($this->GetDefault()); if (!empty($tmpvalue)) { $tree .= " class=\"navlnk\""; } $tree .= " >" . $this->GetQname() . ""; if (is_array($this->subqueues)) { $tree .= ""; } $tree .= "
  • "; return $tree; } /* Even this should take children into consideration */ function build_rules(&$default = false) { $pfq_rule = "queue ". $this->qname; if ($this->GetInterface()) { $pfq_rule .= " on ".get_real_interface($this->GetInterface()); } if ($this->GetBandwidth() && $this->GetBwscale()) { $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); } $tmpvalue = $this->GetQpriority(); if (!empty($tmpvalue)) { $pfq_rule .= " priority " . $this->GetQpriority(); } $tmpvalue = trim($this->GetQlimit()); if (!empty($tmpvalue)) { $pfq_rule .= " qlimit " . $this->GetQlimit(); } if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) { $pfq_rule .= " cbq ( "; $tmpvalue = trim($this->GetRed()); if (!empty($tmpvalue)) { $comma = 1; $pfq_rule .= " red "; } $tmpvalue = trim($this->GetCodel()); if (!empty($tmpvalue)) { $comma = 1; $pfq_rule .= " codel "; } $tmpvalue = trim($this->GetRio()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " rio "; } $tmpvalue = trim($this->GetEcn()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " ecn "; } $tmpvalue = trim($this->GetDefault()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " default "; $default = true; } $tmpvalue = trim($this->GetBorrow()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= ", "; } $pfq_rule .= " borrow "; } $pfq_rule .= " ) "; } if (count($this->subqueues)) { $i = count($this->subqueues); $pfq_rule .= " { "; foreach ($this->subqueues as $qkey => $qnone) { if ($i > 1) { $i--; $pfq_rule .= " {$qkey}, "; } else { $pfq_rule .= " {$qkey} "; } } $pfq_rule .= " } \n"; foreach ($this->subqueues as $q) { $pfq_rule .= $q->build_rules($default); } } $pfq_rule .= " \n"; return $pfq_rule; } function build_form() { $sform = parent::build_form(); $section = new Form_Section(''); $group = new Form_Group('Bandwidth'); $group->add(new Form_Input( 'bandwidth', null, 'number', $this->GetBandwidth() )); $group->add(new Form_Select( 'bandwidthtype', null, $this->GetBwscale(), array('Kb' => 'Kb', 'Mb' => 'Mb', 'Gb' => 'Gb', 'b' => 'b', '%' => '%') )); $group->setHelp('Choose the amount of bandwidth for this queue'); $section->add($group); $section->addInput(new Form_Checkbox( 'borrow', 'Scheduler option', 'Borrow from other queues when available', ($this->GetBorrow() == "on") )); return $sform; } 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['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['name'] = $this->GetQname(); $cflink['description'] = $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['codel'] = trim($this->GetCodel()); if (empty($cflink['codel'])) { unset($cflink['codel']); } $cflink['borrow'] = trim($this->GetBorrow()); if (empty($cflink['borrow'])) { unset($cflink['borrow']); } } } class fairq_queue extends priq_queue { var $hogs; var $buckets; function GetBuckets() { return $this->buckets; } function SetBuckets($buckets) { $this->buckets = $buckets; } function GetHogs() { return $this->hogs; } function SetHogs($hogs) { $this->hogs = $hogs; } function CanHaveChildren() { return false; } function copy_queue($interface, &$cflink) { $cflink['interface'] = $interface; $cflink['qlimit'] = $this->GetQlimit(); $cflink['priority'] = $this->GetQpriority(); $cflink['name'] = $this->GetQname(); $cflink['description'] = $this->GetDescription(); $cflink['bandwidth'] = $this->GetBandwidth(); $cflink['bandwidthtype'] = $this->GetBwscale(); $cflink['enabled'] = $this->GetEnabled(); $cflink['default'] = $this->GetDefault(); $cflink['red'] = $this->GetRed(); $cflink['rio'] = $this->GetRio(); $cflink['ecn'] = $this->GetEcn(); $cflink['buckets'] = $this->GetBuckets(); $cflink['hogs'] = $this->GetHogs(); } /* * Should search even its children */ function &find_queue($interface, $qname) { if ($qname == $this->GetQname()) { return $this; } } function find_parentqueue($interface, $qname) { return; } function delete_queue() { unref_on_altq_queue_list($this->GetQname()); cleanup_queue_from_rules($this->GetQname()); unset_object_by_reference($this->GetLink()); } function validate_input($data, &$input_errors) { parent::validate_input($data, $input_errors); if ($data['priority'] > 255) { $input_errors[] = gettext("Priority must be an integer between 1 and 255."); } $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; default: $mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']); break; } if ($parent->GetAvailableBandwidth() < floatval($myBw)) { $input_errors[] = "The sum of children bandwidth exceeds that of the parent."; } */ } function ReadConfig(&$q) { parent::ReadConfig($q); if (!empty($q['buckets'])) { $this->SetBuckets($q['buckets']); } else { $this->SetBuckets(""); } if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) { $this->SetHogs($q['hogs']); } else { $this->SetHogs(""); } } function build_javascript() { return parent::build_javascript(); } function build_tree() { $tree = "
  • GetInterface()."&queue=" . $this->GetQname()."&action=show"; $tree .= "\" "; $tmpvalue = trim($this->GetDefault()); if (!empty($tmpvalue)) { $tree .= " class=\"navlnk\""; } $tree .= " >" . $this->GetQname() . ""; $tree .= "
  • "; return $tree; } /* Even this should take children into consideration */ function build_rules(&$default = false) { $pfq_rule = "queue ". $this->qname; if ($this->GetInterface()) { $pfq_rule .= " on ".get_real_interface($this->GetInterface()); } if ($this->GetBandwidth() && $this->GetBwscale()) { $pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale(); } $tmpvalue = trim($this->GetQpriority()); if (!empty($tmpvalue)) { $pfq_rule .= " priority " . $this->GetQpriority(); } $tmpvalue = trim($this->GetQlimit()); if (!empty($tmpvalue)) { $pfq_rule .= " qlimit " . $this->GetQlimit(); } if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) { $pfq_rule .= " fairq ( "; $tmpvalue = trim($this->GetRed()); if (!empty($tmpvalue)) { $comma = 1; $pfq_rule .= " red "; } $tmpvalue = trim($this->GetCodel()); if (!empty($tmpvalue)) { $comma = 1; $pfq_rule .= " codel "; } $tmpvalue = trim($this->GetRio()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " rio "; } $tmpvalue = trim($this->GetEcn()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " ecn "; } $tmpvalue = trim($this->GetDefault()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= " ,"; } $comma = 1; $pfq_rule .= " default "; $default = true; } $tmpvalue = trim($this->GetBuckets()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= ", "; } $pfq_rule .= " buckets " . $this->GetBuckets() . " "; } $tmpvalue = trim($this->GetHogs()); if (!empty($tmpvalue)) { if ($comma) { $pfq_rule .= ", "; } $pfq_rule .= " hogs " . $this->GetHogs() . " "; } $pfq_rule .= " ) "; } $pfq_rule .= " \n"; return $pfq_rule; } function build_form() { $form = parent::build_form(); $section = new Form_Section(''); $group = new Form_Group('Bandwidth'); $group->add(new Form_Input( 'bandwidth', null, 'number', $this->GetBandwidth() )); $group->add(new Form_Select( 'bandwidthtype', null, $this->GetBwscale(), array('Kb' => 'Kb', 'Mb' => 'Mb', 'Gb' => 'Gb', 'b' => 'b', '%' => '%') )); $group->setHelp('Choose the amount of bandwidth for this queue'); $section->add($group); $form .= "" . gettext("Scheduler specific options") . ""; $form .= ""; $form .= ""; $form .= "
    "; $form .= "GetBuckets()); if (!empty($tmpvalue)) { $form .= $this->GetBuckets(); } $form .= "\" /> " . gettext("Number of buckets available.") . "
    GetHogs()); if (!empty($tmpvalue)) { $form .= $this->GetHogs(); } $form .= "\" /> " . gettext("Bandwidth limit for hosts to not saturate link.") . "
    "; 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['interface'] = $this->GetInterface(); $cflink['qlimit'] = trim($this->GetQlimit()); if (empty($cflink['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'] = $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['codel'] = trim($this->GetCodel()); if (empty($cflink['codel'])) { unset($cflink['codel']); } $cflink['buckets'] = trim($this->GetBuckets()); if (empty($cflink['buckets'])) { unset($cflink['buckets']); } $cflink['hogs'] = trim($this->GetHogs()); if (empty($cflink['hogs'])) { unset($cflink['hogs']); } } } /* * dummynet(4) wrappers. */ /* * List of respective objects! */ $dummynet_pipe_list = array(); class dummynet_class { var $qname; var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */ var $qlimit; var $description; var $qenabled; var $link; var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */ var $plr; var $buckets; /* mask parameters */ var $mask; var $noerror; /* Accessor functions */ function SetLink($link) { $this->link = $link; } function GetLink() { return $this->link; } function GetMask() { if (!isset($this->mask["type"])) { $this->mask["type"] = "none"; } return $this->mask; } function SetMask($mask) { $this->mask = $mask; } 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 GetQlimit() { return $this->qlimit; } function SetQlimit($limit) { $this->qlimit = $limit; } function GetDescription() { return $this->description; } function SetDescription($str) { $this->description = trim($str); } function GetFirstime() { return $this->firsttime; } function SetFirsttime($number) { $this->firsttime = $number; } function GetBuckets() { return $this->buckets; } function SetBuckets($buckets) { $this->buckets = $buckets; } function SetNumber($number) { $this->qnumber = $number; } function GetNumber() { return $this->qnumber; } function GetPlr() { return $this->plr; } function SetPlr($plr) { $this->plr = $plr; } function build_javascript() { $javascript .= "\n"; return $javascript; } function validate_input($data, &$input_errors) { $reqdfields[] = "bandwidth"; $reqdfieldsn[] = gettext("Bandwidth"); /*$reqdfields[] = "burst"; $reqdfieldsn[] = gettext("Burst"); */ $reqdfields[] = "bandwidthtype"; $reqdfieldsn[] = gettext("Bandwidthtype"); $reqdfields[] = "newname"; $reqdfieldsn[] = gettext("Name"); shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors); if ($data['plr'] && (!is_numeric($data['plr']) || ($data['plr'] < 0) || ($data['plr'] > 1))) { $input_errors[] = gettext("Plr must be a value between 0 and 1."); } if ($data['buckets'] && (!is_numeric($data['buckets']) || ($data['buckets'] < 16) || ($data['buckets'] > 65535))) { $input_errors[] = gettext("Buckets must be an integer between 16 and 65535."); } if ($data['qlimit'] && (!is_numeric($data['qlimit']))) { $input_errors[] = gettext("Queue limit must be an integer"); } 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."); } if (isset($data['maskbits']) && ($data['maskbits'] <> "")) { if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) { $input_errors[] = gettext("IPV4 bit mask must be blank or numeric value between 1 and 32."); } } if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) { if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) { $input_errors[] = gettext("IPV6 bit mask must be blank or numeric value between 1 and 128."); } } } function build_mask_rules(&$pfq_rule) { $mask = $this->GetMask(); if (!empty($mask['type'])) { if ($mask['type'] <> 'none') { $pfq_rule .= " mask"; } switch ($mask['type']) { case 'srcaddress': if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) { $pfq_rule .= " src-ip6 /" . $mask['bitsv6']; } else { $pfq_rule .= " src-ip6 /128"; } if (!empty($mask['bits']) && ($mask['bits'] <> "")) { $pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits'])); } else { $pfq_rule .= " src-ip 0xffffffff"; } break; case 'dstaddress': if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) { $pfq_rule .= " dst-ip6 /" . $mask['bitsv6']; } else { $pfq_rule .= " dst-ip6 /128"; } if (!empty($mask['bits']) && ($mask['bits'] <> "")) { $pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits'])); } else { $pfq_rule .= " dst-ip 0xffffffff"; } break; default: break; } } } } class dnpipe_class extends dummynet_class { var $delay; var $qbandwidth = array(); var $qbandwidthtype; /* This is here to help on form building and building rules/lists */ var $subqueues = array(); function CanHaveChildren() { return true; } function SetDelay($delay) { $this->delay = $delay; } function GetDelay() { return $this->delay; } function delete_queue() { cleanup_dnqueue_from_rules($this->GetQname()); foreach ($this->subqueues as $q) { $q->delete_queue(); } unset_dn_object_by_reference($this->GetLink()); @pfSense_pipe_action("pipe delete " . $this->GetNumber()); } function GetBandwidth() { return $this->qbandwidth; } function SetBandwidth($bandwidth) { $this->qbandwidth = $bandwidth; } function GetBurst() { return $this->qburst; } function SetBurst($burst) { $this->qburst = $burst; } function &add_queue($interface, &$queue, &$path, &$input_errors) { if (!is_array($this->subqueues)) { $this->subqueues = array(); } $q =& new dnqueue_class(); $q->SetLink($path); $q->SetEnabled("on"); $q->SetPipe($this->GetQname()); $q->SetParent($this); $q->ReadConfig($queue); $q->validate_input($queue, $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; } $number = dnqueue_find_nextnumber(); $q->SetNumber($number); $this->subqueues[$q->GetQname()] = &$q; return $q; } function &get_queue_list(&$q = null) { $qlist = array(); $qlist[$this->GetQname()] = $this->GetNumber(); if (is_array($this->subqueues)) { foreach ($this->subqueues as $queue) { $queue->get_queue_list($qlist); } } return $qlist; } /* * Should search even its children */ function &find_queue($pipe, $qname) { if ($qname == $this->GetQname()) { return $this; } foreach ($this->subqueues as $q) { $result =& $q->find_queue("", $qname); if ($result) { return $result; } } } function &find_parentqueue($pipe, $qname) { return NULL; } function validate_input($data, &$input_errors) { parent::validate_input($data, $input_errors); $schedule = 0; $schedulenone = 0; $entries = 0; /* XXX: Really no better way? */ for ($i = 0; $i < 2900; $i++) { if (!empty($data["bwsched{$i}"])) { if ($data["bwsched{$i}"] != "none") { $schedule++; } else { $schedulenone++; } } if (!empty($data["bandwidth{$i}"])) { if (!is_numeric($data["bandwidth{$i}"])) { $input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]); } else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) { $input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]); } else { $entries++; } } } if ($schedule == 0 && $entries > 1) { $input_errors[] = gettext("You need to specify a schedule for every additional entry"); } if ($schedulenone > 0 && $entries > 1) { $input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected"); } if ($entries == 0) { $input_errors[] = gettext("At least one bw specification is necessary"); } if ($data['delay'] && (!is_numeric($data['delay']))) { $input_errors[] = gettext("Delay must be an integer."); } } 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 (!empty($_POST)) { $bandwidth = array(); /* XXX: Really no better way? */ for ($i = 0; $i < 2900; $i++) { if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") { $bw = array(); $bw['bw'] = $q["bandwidth{$i}"]; $bw['burst'] = $q["burst{$i}"]; if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) { $bw['bwscale'] = $q["bwtype{$i}"]; } if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) { $bw['bwsched'] = $q["bwsched{$i}"]; } $bandwidth[] = $bw; } } $this->SetBandwidth($bandwidth); } if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) { $this->SetBandwidth($q['bandwidth']['item']); $this->SetBurst($q['burst']['item']); } 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['delay']) && $q['delay'] <> "") { $this->SetDelay($q['delay']); } else { $this->SetDelay(0); } if (isset($q['description']) && $q['description'] <> "") { $this->SetDescription($q['description']); } else { $this->SetDescription(""); } $this->SetEnabled($q['enabled']); } function build_tree() { $tree = "
  • GetQname() ."&queue=".$this->GetQname() ."&action=show\">"; $tree .= $this->GetQname() . ""; if (is_array($this->subqueues)) { $tree .= ""; } $tree .= "
  • "; return $tree; } function build_rules() { global $config, $time_based_rules; if ($this->GetEnabled() == "") { return; } $pfq_rule = "\npipe ". $this->GetNumber() . " config "; $found = false; $bandwidth = $this->GetBandwidth(); if (is_array($bandwidth)) { foreach ($bandwidth as $bw) { if ($bw['bwsched'] != "none") { $time_based_rules = true; if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { foreach ($config['schedules']['schedule'] as $schedule) { if ($bw['bwsched'] == $schedule['name']) { if (filter_get_time_based_rule_status($schedule)) { $pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale']; if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) { $pfq_rule .= " burst ".trim($bw['burst']); } $found = true; break; } } } } else { $pfq_rule .= " bw 0"; $found = true; break; } } else { $pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale']; if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) { $pfq_rule .= " burst ".trim($bw['burst']); } $found = true; break; } } if ($found == false) { $pfq_rule .= " bw 0"; } } else { $pfq_rule .= " bw 0"; } if ($this->GetQlimit()) { $pfq_rule .= " queue " . $this->GetQlimit(); } if ($this->GetPlr()) { $pfq_rule .= " plr " . $this->GetPlr(); } if ($this->GetBuckets()) { $pfq_rule .= " buckets " . $this->GetBuckets(); } if ($this->GetDelay()) { $pfq_rule .= " delay " . $this->GetDelay(); } $this->build_mask_rules($pfq_rule); $pfq_rule .= "\n"; if (!empty($this->subqueues) && count($this->subqueues) > 0) { foreach ($this->subqueues as $q) { $pfq_rule .= $q->build_rules(); } } $pfq_rule .= " \n"; return $pfq_rule; } function update_dn_data(&$data) { $this->ReadConfig($data); } function build_javascript() { global $g, $config; $javasr = parent::build_javascript(); //build list of schedules $schedules = ""; if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { foreach ($config['schedules']['schedule'] as $schedule) { if ($schedule['name'] <> "") { $schedules .= ""; } } } $bwopt = ""; foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) { $bwopt .= ""; } $javasr .= << //"; cell2.innerHTML = ""; cell3.innerHTML = ""; cell4.innerHTML = 'Remove'; }); })(); function removeBwRow(el) { var d = el.parentNode.parentNode.rowIndex; document.getElementById('maintable').deleteRow(d); } //]]> EOD; return $javasr; } // Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText // The table has been "Bootstrapped" to match the web design while maintaining compatibility with // with the javascript in this class function build_bwtable() { global $config; $bandwidth = $this->GetBandwidth(); //build list of schedules $schedules = array(); $schedules[] = "none";//leave none to leave rule enabled all the time if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { foreach ($config['schedules']['schedule'] as $schedule) { if ($schedule['name'] != "") { $schedules[] = $schedule['name']; } } } $form = '
    '; $form .= ''; $form .= ""; $form .= ""; //$form .= ""; $form .= ""; $form .= ""; $form .= ""; $form .= ""; $form .= ""; // If there are no bandwidths defined, make a blank one for convenience if(empty($bandwidth)) $bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none')); if (is_array($bandwidth)) { foreach ($bandwidth as $bwidx => $bw) { $form .= ''; $form .= '"; $form .= '"; $form .= '"; } } $form .= "
    Bandwidth
    Burst
    Bw typeSchedule
    '; $form .= ""; //$form .= ""; //$form .= ""; $form .= "'; $form .= ""; $form .= "'; $form .= '' . gettext('Remove') . ''; $form .= "

    "; $form .= ''; $form .= gettext("Add another schedule") . ""; return($form); } function build_form() { global $g, $config, $pipe, $action, $qname; //build list of schedules $schedules = array(); $schedules[] = "none";//leave none to leave rule enabled all the time if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { foreach ($config['schedules']['schedule'] as $schedule) { if ($schedule['name'] <> "") { $schedules[] = $schedule['name']; } } } $sform = new Form(); $section = new Form_Section('Limiters'); $section->addInput(new Form_Checkbox( 'enabled', 'Enable', 'Enable limiter and its children', ($this->GetEnabled() == "on"), 'on' )); $section->addInput(new Form_Input( 'newname', 'Name', 'text', $this->GetQname() )); $section->addInput(new Form_Input( 'name', null, 'hidden', $this->GetQname() )); if ($this->GetNumber() > 0) { $section->addInput(new Form_Input( 'number', null, 'hidden', $this->GetNumber() )); } $bandwidth = $this->GetBandwidth(); // Delete a row // if(isset($_GET['delbwrow']) && (count($bandwidth) > 0)) // unset($bandwidth[$_GET['delbwrow']]); // Add a row // if($_GET['newbwrow']) { // array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') )); // } if (is_array($bandwidth)) { $section->addInput(new Form_StaticText( 'Bandwidth', $this->build_bwtable() )); } $mask = $this->GetMask(); $section->addInput(new Form_Select( 'scheduler', 'Mask', $mask['type'], array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses') ))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' . 'and queue size given above will be created for each source/destination IP address encountered, respectively. ' . 'This makes it possible to easily specify bandwidth limits per host.'); $group = new Form_Group(null); $group->add(new Form_Select( 'maskbits', null, $mask['bits'], array_combine(range(32, 1, -1), range(32, 1, -1)) ))->setHelp('IPV4 mask bits' . '
    ' . '255.255.255.255/?'); $group->add(new Form_Select( 'maskbitsv6', null, $mask['bitsv6'], array_combine(range(128, 1, -1), range(128, 1, -1)) ))->setHelp('IPV6 mask bits' . '
    ' . 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?'); $section->add($group); $section->addInput(new Form_Input( 'description', 'Description', 'text', $this->GetDescription() ))->setHelp('You may enter a description here for your reference (not parsed).'); $sform->add($section); $section = new Form_Section('Advanced options'); $section->addInput(new Form_Input( 'delay', 'Delay (ms)', 'text', $this->GetDelay() > 0 ? $this->GetDelay():null ))->setHelp('In most cases, you should specify 0 here (or leave the field empty)'); $section->addInput(new Form_Input( 'plr', 'Packet Loss Rate', 'number', $this->GetPlr(), ['step' => '0.001', 'min' => '0.000'] ))->setHelp('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'); $section->addInput(new Form_Input( 'qlimit', 'Queue size (slots)', 'number', $this->GetQlimit() ))->setHelp('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.'); $section->addInput(new Form_Input( 'buckets', 'Bucket size (slots)', 'number', $this->GetBuckets() ))->setHelp('In most cases, you should leave this field empty. It increases the hash size set'); $sform->add($section); return($sform); } 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 = "
  • GetQname() ."&queue=" . $this->GetQname() ."&action=show\">"; $tree .= $this->GetQname() . ""; $tree .= "
  • "; return $tree; } function build_rules() { if ($this->GetEnabled() == "") { return; } $parent =& $this->GetParent(); $pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber(); if ($this->GetQlimit()) { $pfq_rule .= " queue " . $this->GetQlimit(); } if ($this->GetWeight()) { $pfq_rule .= " weight " . $this->GetWeight(); } if ($this->GetBuckets()) { $pfq_rule .= " buckets " . $this->GetBuckets(); } $this->build_mask_rules($pfq_rule); $pfq_rule .= "\n"; return $pfq_rule; } function build_javascript() { return parent::build_javascript(); } function build_form() { global $g, $config, $pipe, $action, $qname; //build list of schedules $schedules = array(); $schedules[] = "none";//leave none to leave rule enabled all the time if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) { foreach ($config['schedules']['schedule'] as $schedule) { if ($schedule['name'] <> "") { $schedules[] = $schedule['name']; } } } $sform = new Form(); $section = new Form_Section('Limiters'); $section->addInput(new Form_Checkbox( 'enabled', 'Enable', 'Enable this queue', ($this->GetEnabled() == "on"), 'on' )); $section->addInput(new Form_Input( 'newname', 'Name', 'text', $this->GetQname() )); $section->addInput(new Form_Input( 'name', null, 'hidden', $this->GetQname() )); if ($this->GetNumber() > 0) { $section->addInput(new Form_Input( 'number', null, 'hidden', $this->GetNumber() )); } $bandwidth = $this->GetBandwidth(); // Delete a row if(isset($_GET['delbwrow']) && (count($bandwidth) > 0)) unset($bandwidth[$_GET['delbwrow']]); // Add a row if($_GET['newbwrow']) { array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') )); } if (is_array($bandwidth)) { $row = 0; $numrows = count($bandwidth) - 1; if($numrows >= 0) { foreach ($bandwidth as $bwidx => $bw) { $group = new Form_Group($row == 0 ? 'Bandwidth':null); $group->add(new Form_Input( 'bandwidth' . $bwidx, null, 'text', $bw['bw'] ))->setHelp($row == $numrows ? 'Bandwidth':null); $group->add(new Form_Select( 'bwtype' . $bwidx, null, $bw['bwscale'], array('Kb' => 'Kbit/s', 'Mb' => 'Mbit/s', 'Gb' => 'Gbit/s', 'b' => 'Bit/s') ))->setHelp($row == $numrows ? 'Bw Type':null);; $group->add(new Form_Select( 'bwsched' . $bwidx, null, $bw['bwsched'], $schedules ))->setHelp($row == $numrows ? 'Schedule':null);; $group->add(new Form_Button( 'delete' + $bwidx, 'Delete', 'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&delbwrow=' . $bwidx ))->removeClass('btn-primary')->addClass('btn-danger btn-sm'); if($row == $numrows) $group->setHelp('Bandwidth is the rate (e.g. Mbit/s) to which traffic in this limiter will be restricted.'); $section->add($group); $row++; } } else { // The $bandwidth array exists, but is empty $section->addInput(new Form_StaticText( 'Bandwidth', 'No schedules configured for this limiter.' )); } $section->addInput(new Form_Button( 'addsched', 'Add new schedule', 'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&newbwrow=yes' ))->removeClass('btn-primary')->addClass('btn-success btn-sm'); } $mask = $this->GetMask(); $section->addInput(new Form_Select( 'scheduler', 'Mask', $mask['type'], array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses') ))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' . 'and queue size given above will be created for each source/destination IP address encountered, respectively. ' . 'This makes it possible to easily specify bandwidth limits per host.'); $group = new Form_Group(null); $group->add(new Form_Select( 'maskbits', null, $mask['bits'], array_combine(range(32, 1, -1), range(32, 1, -1)) ))->setHelp('IPV4 mask bits' . '
    ' . '255.255.255.255/?'); $group->add(new Form_Select( 'maskbitsv6', null, $mask['bitsv6'], array_combine(range(128, 1, -1), range(128, 1, -1)) ))->setHelp('IPV6 mask bits' . '
    ' . 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?'); $section->add($group); $section->addInput(new Form_Input( 'description', 'Description', 'text', $this->GetDescription() ))->setHelp('You may enter a description here for your reference (not parsed).'); $sform->add($section); $section = new Form_Section('Advanced options'); $section->addInput(new Form_Input( 'weight', 'Weight', 'number', $this->GetWeight(), ['min' => '1', 'max' => '100'] ))->setHelp('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'); $section->addInput(new Form_Input( 'plr', 'Packet Loss Rate', 'number', $this->GetPlr(), ['step' => '0.001', 'min' => '0.000'] ))->setHelp('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'); $section->addInput(new Form_Input( 'qlimit', 'Queue size (slots)', 'number', $this->GetQlimit() ))->setHelp('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.'); $section->addInput(new Form_Input( 'buckets', 'Bucket size (slots)', 'number', $this->GetBuckets() ))->setHelp('In most cases, you should leave this field empty. It increases the hash size set'); $section->addInput(new Form_Input( 'pipe', null, 'hidden', $this->GetPipe() )); $sform->add($section); return($sform); } 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,structure,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 = "
  • GetRName() ."&action=show\">"; $tree .= $this->GetRName() . ""; $tree .= "
  • "; return $tree; } function build_form() { $form = new Form(new Form_Button( 'Submit', 'Save' )); $section = new Form_Section('Traffic Shaper'); $section->addInput(new Form_Checkbox( 'enabled', 'Enable/Disable', 'Enable/disable discipline and its children', ($this->GetREnabled() == "on"), 'on' )); $section->addInput(new Form_Input( 'container', 'Name', 'text', $this->GetRName() )); $section->addInput(new Form_Input( 'description', 'Description', 'text', $this->GetRDescription() ))->setHelp('You may enter a description here for your reference (not parsed).'); $form->add($section); 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 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 functions * 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 .= "
  • ".$shDescr."
  • "; } } return $tree; } function filter_generate_altq_queues() { global $altq_list_queues; read_altq_config(); $altq_rules = ""; foreach ($altq_list_queues as $altq) { $altq_rules .= $altq->build_rules(); } return $altq_rules; } function dnqueue_find_nextnumber() { global $dummynet_pipe_list; $dnused = array(); if (is_array($dummynet_pipe_list)) { foreach ($dummynet_pipe_list as $dn) { $tmplist =& $dn->get_queue_list(); foreach ($tmplist as $qname => $link) { if ($link[0] == "?") { $dnused[$qname] = substr($link, 1); } } } } sort($dnused, SORT_NUMERIC); $dnnumber = 0; $found = false; foreach ($dnused as $dnnum) { if (($dnnum - $dnnumber) > 1) { $dnnumber = $dnnum - 1; $found = true; break; } else { $dnnumber = $dnnum; } } if ($found == false) { $dnnumber++; } unset($dnused, $dnnum, $found); return $dnnumber; } function dnpipe_find_nextnumber() { global $dummynet_pipe_list; $dnused = array(); foreach ($dummynet_pipe_list as $dn) { $dnused[] = $dn->GetNumber(); } sort($dnused, SORT_NUMERIC); $dnnumber = 0; $found = false; foreach ($dnused as $dnnum) { if (($dnnum - $dnnumber) > 1) { $dnnumber = $dnnum - 1; $found = true; break; } else { $dnnumber = $dnnum; } } if ($found == false) { $dnnumber++; } unset($dnused, $dnnum, $found); return $dnnumber; } function filter_generate_dummynet_rules() { global $g, $dummynet_pipe_list; read_dummynet_config(); $dn_rules = ""; foreach ($dummynet_pipe_list as $dn) { $dn_rules .= $dn->build_rules(); } if (!empty($dn_rules)) { if (!is_module_loaded("dummynet.ko")) { mwexec("/sbin/kldload dummynet"); set_sysctl(array( "net.inet.ip.dummynet.io_fast" => "1", "net.inet.ip.dummynet.hash_size" => "256" )); } file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules); mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter"); } } function build_iface_without_this_queue($iface, $qname) { global $g, $altq_list_queues; global $shaperIFlist; $altq =& $altq_list_queues[$iface]; if ($altq) { $scheduler = $altq->GetScheduler(); } $form = '
    '; $form .= '
    '; $form .= ' ' . $shaperIFlist[$iface] . ''; $form .= '
    '; $form .= '
    '; $form .= $scheduler; $form .= '
    '; $form .= '
    '; $form .= 'Clone'; $form .= '
    '; $form .= '
    '; $form .= ''; $form .= gettext("Clone shaper on the I/F") . ''; $form .= '
    '; $form .= '
    '; return $form; } $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."); $dn_default_shaper_msg = $default_shaper_msg; ?>