mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 00:07:26 +00:00
shaper: move pipe & queue configuration to dnctl service (#8404)
if no shaper (ipfw) rules are present, or these rules are disabled, ipfw will be disabled as well (firewall_enable="NO" and rc.ipfw onestop). Traffic shaped via pf will not show up in the stats output of dnctl pipe|queue|sched show. Also, there is currently no logic to associate pipes/queues with pf rules.
This commit is contained in:
parent
3a1b88bf90
commit
3bf818348c
5
plist
5
plist
@ -1273,6 +1273,7 @@
|
||||
/usr/local/opnsense/scripts/routes/show_routes.py
|
||||
/usr/local/opnsense/scripts/shaper/dummynet_stats.py
|
||||
/usr/local/opnsense/scripts/shaper/lib/__init__.py
|
||||
/usr/local/opnsense/scripts/shaper/setup.sh
|
||||
/usr/local/opnsense/scripts/shaper/update_tables
|
||||
/usr/local/opnsense/scripts/shell/banner.php
|
||||
/usr/local/opnsense/scripts/shell/defaults.php
|
||||
@ -1364,6 +1365,7 @@
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_netflow.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_openssh.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_openvpn.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_shaper.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_syslog.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_system.conf
|
||||
/usr/local/opnsense/service/conf/actions.d/actions_template.conf
|
||||
@ -1452,6 +1454,9 @@
|
||||
/usr/local/opnsense/service/templates/OPNsense/Sample/sub1/example_sub1.txt
|
||||
/usr/local/opnsense/service/templates/OPNsense/Sample/sub2/+TARGETS
|
||||
/usr/local/opnsense/service/templates/OPNsense/Sample/sub2/example_sub2.txt
|
||||
/usr/local/opnsense/service/templates/OPNsense/Shaper/+TARGETS
|
||||
/usr/local/opnsense/service/templates/OPNsense/Shaper/dnctl.conf
|
||||
/usr/local/opnsense/service/templates/OPNsense/Shaper/rc.conf.d
|
||||
/usr/local/opnsense/service/templates/OPNsense/Syslog/+TARGETS
|
||||
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/README
|
||||
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/audit.conf
|
||||
|
||||
@ -27,10 +27,6 @@
|
||||
# script to glue standard ipfw rc scripting to OPNsense ruleset
|
||||
# see auto generated file /etc/rc.conf.d/ipfw for details
|
||||
|
||||
# sysctl settings
|
||||
sysctl net.inet.ip.dummynet.io_fast=1 > /dev/null
|
||||
sysctl net.inet.ip.dummynet.hash_size=256 > /dev/null
|
||||
|
||||
# reload ipfw rules
|
||||
ipfw -q /usr/local/etc/ipfw.rules
|
||||
# load tables
|
||||
|
||||
@ -41,24 +41,29 @@ use OPNsense\TrafficShaper\TrafficShaper;
|
||||
class ServiceController extends ApiControllerBase
|
||||
{
|
||||
/**
|
||||
* reconfigure ipfw, generate config and reload
|
||||
* reconfigure shaper/ipfw, generate config and reload
|
||||
*/
|
||||
public function reconfigureAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$backend->configdRun('template reload OPNsense/Shaper');
|
||||
$backend->configdRun('template reload OPNsense/IPFW');
|
||||
$bckresult = trim($backend->configdRun("ipfw reload"));
|
||||
if ($bckresult == "OK") {
|
||||
$status = "ok";
|
||||
} else {
|
||||
$status = "error reloading shaper (" . $bckresult . ")";
|
||||
|
||||
$result = trim($backend->configdRun("shaper reload"));
|
||||
if ($result != "OK") {
|
||||
return ["status" => "error reloading shaper (" . $result . ")"];
|
||||
}
|
||||
|
||||
return array("status" => $status);
|
||||
} else {
|
||||
return array("status" => "failed");
|
||||
$result = trim($backend->configdRun("ipfw reload"));
|
||||
if ($result != "OK") {
|
||||
return ["status" => "error reloading ipfw (" . $result . ")"];
|
||||
}
|
||||
|
||||
return ["status" => "ok"];
|
||||
}
|
||||
|
||||
return ["status" => "failed"];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,10 +74,11 @@ class ServiceController extends ApiControllerBase
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$status = trim($backend->configdRun("ipfw flush"));
|
||||
$status = trim($backend->configdRun("shaper reload"));
|
||||
$status = trim($backend->configdRun("ipfw reload"));
|
||||
return array("status" => $status);
|
||||
return ["status" => $status];
|
||||
} else {
|
||||
return array("status" => "failed");
|
||||
return ["status" => "failed"];
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,15 +87,14 @@ class ServiceController extends ApiControllerBase
|
||||
*/
|
||||
public function statisticsAction()
|
||||
{
|
||||
$result = array("status" => "failed");
|
||||
$result = ["status" => "failed"];
|
||||
if ($this->request->isGet()) {
|
||||
$ipfwstats = json_decode((new Backend())->configdRun("ipfw stats"), true);
|
||||
$ipfwstats = json_decode((new Backend())->configdRun("shaper stats"), true);
|
||||
if ($ipfwstats != null) {
|
||||
// ipfw stats are structured as they would be using the various ipfw commands, let's reformat
|
||||
// into something easier to handle from the UI and attach model data.
|
||||
// reformat into something easier to handle from the UI and attach model data.
|
||||
$result['status'] = "ok";
|
||||
$result['items'] = array();
|
||||
$pipenrs = array();
|
||||
$result['items'] = [];
|
||||
$pipenrs = [];
|
||||
if (!empty($ipfwstats['pipes'])) {
|
||||
$shaperModel = new TrafficShaper();
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ def trim_dict(payload):
|
||||
|
||||
def parse_ipfw_pipes():
|
||||
result = dict()
|
||||
pipetxt = subprocess.run(['/sbin/ipfw', 'pipe', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
pipetxt = subprocess.run(['/sbin/dnctl', 'pipe', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
current_pipe = None
|
||||
current_pipe_header = False
|
||||
for line in ("%s\n000000X" % pipetxt).split('\n'):
|
||||
@ -125,7 +125,7 @@ def parse_ipfw_pipes():
|
||||
|
||||
def parse_ipfw_queues():
|
||||
result = dict()
|
||||
queuetxt = subprocess.run(['/sbin/ipfw', 'queue', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
queuetxt = subprocess.run(['/sbin/dnctl', 'queue', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
current_queue = None
|
||||
current_queue_header = False
|
||||
for line in ("%s\nq000000X" % queuetxt).split('\n'):
|
||||
@ -149,7 +149,7 @@ def parse_ipfw_queues():
|
||||
|
||||
def parse_ipfw_scheds():
|
||||
result = dict()
|
||||
schedtxt = subprocess.run(['/sbin/ipfw', 'sched', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
schedtxt = subprocess.run(['/sbin/dnctl', 'sched', 'show'], capture_output=True, text=True).stdout.strip()
|
||||
current_sched = None
|
||||
for line in ("%s\n000000X" % schedtxt).split('\n'):
|
||||
if len(line) == 0:
|
||||
|
||||
29
src/opnsense/scripts/shaper/setup.sh
Executable file
29
src/opnsense/scripts/shaper/setup.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2025 Deciso B.V.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# sysctl settings
|
||||
sysctl net.inet.ip.dummynet.io_fast=1 > /dev/null
|
||||
sysctl net.inet.ip.dummynet.hash_size=256 > /dev/null
|
||||
@ -1,5 +1,5 @@
|
||||
[reload]
|
||||
command:/etc/rc.d/ipfw onestart; /usr/local/etc/rc.ipfw.postload
|
||||
command:/etc/rc.d/ipfw enabled && /etc/rc.d/ipfw start || /etc/rc.d/ipfw onestop; /usr/local/etc/rc.ipfw.postload
|
||||
parameters:
|
||||
type:script
|
||||
message:restarting ipfw
|
||||
@ -9,9 +9,3 @@ command:/usr/local/etc/rc.ipfw.flush_all
|
||||
parameters:
|
||||
type:script
|
||||
message:flush all ipfw rules
|
||||
|
||||
[stats]
|
||||
command:/usr/local/opnsense/scripts/shaper/dummynet_stats.py
|
||||
parameters:
|
||||
type:script_output
|
||||
message:request dummynet stats
|
||||
|
||||
11
src/opnsense/service/conf/actions.d/actions_shaper.conf
Normal file
11
src/opnsense/service/conf/actions.d/actions_shaper.conf
Normal file
@ -0,0 +1,11 @@
|
||||
[reload]
|
||||
command:/etc/rc.d/dnctl start
|
||||
parameters:
|
||||
type:script
|
||||
message:restarting dummynet
|
||||
|
||||
[stats]
|
||||
command:/usr/local/opnsense/scripts/shaper/dummynet_stats.py
|
||||
parameters:
|
||||
type:script_output
|
||||
message:request dummynet stats
|
||||
@ -6,53 +6,6 @@
|
||||
#======================================================================================
|
||||
flush
|
||||
|
||||
#======================================================================================
|
||||
# define dummynet pipes
|
||||
#======================================================================================
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% for pipe in helpers.toList('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
pipe {{ pipe.number }} config bw {{ pipe.bandwidth }}{{ pipe.bandwidthMetric }}/s{%
|
||||
if pipe.queue %} queue {{ pipe.queue }}{%
|
||||
if pipe.queueMetric != 'slots' %}{{pipe.queueMetric}}{% endif %}{% endif
|
||||
%}{% if pipe.buckets %} buckets {{ pipe.buckets }}{% endif
|
||||
%}{% if pipe.mask != 'none' %} mask {{ pipe.mask }} 0xffffffff {% endif %}{%
|
||||
if pipe.delay|default('') != '' %} delay {{pipe.delay}} {% endif %} type {%
|
||||
if pipe.scheduler|default('') != '' %} {{pipe.scheduler}} {% else %} wf2q+ {% endif %}{%
|
||||
if pipe.codel_enable|default('0') == '1' and pipe.scheduler != 'fq_codel' %} codel {% endif %}{%
|
||||
if pipe.codel_enable|default('0') == '1' or pipe.scheduler == 'fq_codel' %}{%
|
||||
if pipe.codel_target|default('') != ''%} target {{pipe.codel_target}} {% endif %}{%
|
||||
if pipe.codel_interval|default('') != ''%} interval {{pipe.codel_interval}} {% endif %}{%
|
||||
if pipe.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %} {%
|
||||
if pipe.scheduler == 'fq_codel' %} {%
|
||||
if pipe.fqcodel_quantum|default('') != '' %} quantum {{pipe.fqcodel_quantum}} {% endif %} {%
|
||||
if pipe.fqcodel_limit|default('') != '' %} limit {{pipe.fqcodel_limit}} {% endif %} {%
|
||||
if pipe.fqcodel_flows|default('') != '' %} flows {{pipe.fqcodel_flows}} {% endif %}
|
||||
{% endif %}{%
|
||||
elif pipe.pie_enable|default('0') == '1' and pipe.scheduler != 'fq_pie' %} pie {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
#======================================================================================
|
||||
# define dummynet queues
|
||||
#======================================================================================
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.queues.queue') %}
|
||||
{% for queue in helpers.toList('OPNsense.TrafficShaper.queues.queue') %}
|
||||
{% if helpers.getUUIDtag(queue.pipe) in ['pipe'] %}
|
||||
queue {{ queue.number }} config pipe {{ helpers.getUUID(queue.pipe).number
|
||||
}}{% if queue.buckets %} buckets {{ queue.buckets }}{% endif %}{% if queue.mask != 'none' %} mask {{ queue.mask }} 0xffffffff {% endif %} weight {{ queue.weight }}{%
|
||||
if queue.codel_enable|default('0') == '1' %} codel {%
|
||||
if queue.codel_target|default('') != ''%} target {{queue.codel_target}} {% endif %}{%
|
||||
if queue.codel_interval|default('') != ''%} interval {{queue.codel_interval}} {% endif %}{%
|
||||
if queue.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %}{%
|
||||
elif queue.pie_enable|default('0') == '1' %} pie
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
#======================================================================================
|
||||
# general purpose rules 1...1000
|
||||
#======================================================================================
|
||||
|
||||
@ -7,17 +7,16 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{# collect enabled #}
|
||||
{% set shapers = [] %}
|
||||
{% set rules = [] %}
|
||||
{% if helpers.exists('OPNsense.TrafficShaper') %}
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% for pipe in helpers.toList('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% if pipe.enabled|default('0') == '1' %}
|
||||
{% do shapers.append(cp_key) %}
|
||||
{% endif%}
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.rules.rule') %}
|
||||
{% for rule in helpers.toList('OPNsense.TrafficShaper.rules.rule') %}
|
||||
{% if rule.enabled|default("0") == '1' %}
|
||||
{% do rules.append(rule) %}
|
||||
{% endif %}
|
||||
{% endfor%}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
dummynet_enable="YES"
|
||||
firewall_enable="{% if shapers or cp_zones %}YES{% else %}NO{% endif %}"
|
||||
firewall_enable="{% if cp_zones or rules %}YES{% else %}NO{% endif %}"
|
||||
firewall_script="/usr/local/etc/rc.ipfw"
|
||||
ipfw_defer="YES"
|
||||
|
||||
2
src/opnsense/service/templates/OPNsense/Shaper/+TARGETS
Normal file
2
src/opnsense/service/templates/OPNsense/Shaper/+TARGETS
Normal file
@ -0,0 +1,2 @@
|
||||
rc.conf.d:/etc/rc.conf.d/dnctl
|
||||
dnctl.conf:/usr/local/etc/dnctl.conf
|
||||
45
src/opnsense/service/templates/OPNsense/Shaper/dnctl.conf
Normal file
45
src/opnsense/service/templates/OPNsense/Shaper/dnctl.conf
Normal file
@ -0,0 +1,45 @@
|
||||
#======================================================================================
|
||||
# define dummynet pipes
|
||||
#======================================================================================
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% for pipe in helpers.toList('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
pipe {{ pipe.number }} config bw {{ pipe.bandwidth }}{{ pipe.bandwidthMetric }}/s{%
|
||||
if pipe.queue %} queue {{ pipe.queue }}{%
|
||||
if pipe.queueMetric != 'slots' %}{{pipe.queueMetric}}{% endif %}{% endif
|
||||
%}{% if pipe.buckets %} buckets {{ pipe.buckets }}{% endif
|
||||
%}{% if pipe.mask != 'none' %} mask {{ pipe.mask }} 0xffffffff {% endif %}{%
|
||||
if pipe.delay|default('') != '' %} delay {{pipe.delay}} {% endif %} type {%
|
||||
if pipe.scheduler|default('') != '' %} {{pipe.scheduler}} {% else %} wf2q+ {% endif %}{%
|
||||
if pipe.codel_enable|default('0') == '1' and pipe.scheduler != 'fq_codel' %} codel {% endif %}{%
|
||||
if pipe.codel_enable|default('0') == '1' or pipe.scheduler == 'fq_codel' %}{%
|
||||
if pipe.codel_target|default('') != ''%} target {{pipe.codel_target}} {% endif %}{%
|
||||
if pipe.codel_interval|default('') != ''%} interval {{pipe.codel_interval}} {% endif %}{%
|
||||
if pipe.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %} {%
|
||||
if pipe.scheduler == 'fq_codel' %} {%
|
||||
if pipe.fqcodel_quantum|default('') != '' %} quantum {{pipe.fqcodel_quantum}} {% endif %} {%
|
||||
if pipe.fqcodel_limit|default('') != '' %} limit {{pipe.fqcodel_limit}} {% endif %} {%
|
||||
if pipe.fqcodel_flows|default('') != '' %} flows {{pipe.fqcodel_flows}} {% endif %}
|
||||
{% endif %}{%
|
||||
elif pipe.pie_enable|default('0') == '1' and pipe.scheduler != 'fq_pie' %} pie {% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
#======================================================================================
|
||||
# define dummynet queues
|
||||
#======================================================================================
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.queues.queue') %}
|
||||
{% for queue in helpers.toList('OPNsense.TrafficShaper.queues.queue') %}
|
||||
{% if helpers.getUUIDtag(queue.pipe) in ['pipe'] %}
|
||||
queue {{ queue.number }} config pipe {{ helpers.getUUID(queue.pipe).number
|
||||
}}{% if queue.buckets %} buckets {{ queue.buckets }}{% endif %}{% if queue.mask != 'none' %} mask {{ queue.mask }} 0xffffffff {% endif %} weight {{ queue.weight }}{%
|
||||
if queue.codel_enable|default('0') == '1' %} codel {%
|
||||
if queue.codel_target|default('') != ''%} target {{queue.codel_target}} {% endif %}{%
|
||||
if queue.codel_interval|default('') != ''%} interval {{queue.codel_interval}} {% endif %}{%
|
||||
if queue.codel_ecn_enable|default('0') == '1'%} ecn {% else %} noecn {% endif %}{%
|
||||
elif queue.pie_enable|default('0') == '1' %} pie
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
12
src/opnsense/service/templates/OPNsense/Shaper/rc.conf.d
Normal file
12
src/opnsense/service/templates/OPNsense/Shaper/rc.conf.d
Normal file
@ -0,0 +1,12 @@
|
||||
{% set isEnabled=[] %}
|
||||
{% if helpers.exists('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% for pipe in helpers.toList('OPNsense.TrafficShaper.pipes.pipe') %}
|
||||
{% if pipe.enabled|default('0') == '1' %}
|
||||
{% do isEnabled.append(pipe) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
dummynet_enable="YES"
|
||||
dnctl_enable="{%if isEnabled %}YES{% else %}NO{% endif %}"
|
||||
dnctl_rules="/usr/local/etc/dnctl.conf"
|
||||
dnctl_setup="/usr/local/opnsense/scripts/shaper/setup.sh"
|
||||
Loading…
x
Reference in New Issue
Block a user