diff --git a/net/firewall/src/etc/inc/plugins.inc.d/pfplugin.inc b/net/firewall/src/etc/inc/plugins.inc.d/pfplugin.inc
index cdb25a5de..3c9aa2fce 100644
--- a/net/firewall/src/etc/inc/plugins.inc.d/pfplugin.inc
+++ b/net/firewall/src/etc/inc/plugins.inc.d/pfplugin.inc
@@ -42,4 +42,7 @@ function pfplugin_firewall($fw)
foreach ($mdlFilter->snatrules->rule->sortedBy(["sequence"]) as $key => $rule) {
$fw->registerSNatRule(50, $rule->serialize());
}
+ foreach ($mdlFilter->npt->rule->sortedBy(["sequence"]) as $key => $rule) {
+ $fw->registerNptRule(50, $rule->serialize());
+ }
}
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterBaseController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterBaseController.php
index 789ae9a89..3162d985f 100644
--- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterBaseController.php
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterBaseController.php
@@ -30,6 +30,7 @@ namespace OPNsense\Firewall\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
use OPNsense\Core\Backend;
use OPNsense\Core\Config;
+use OPNsense\Firewall\Category;
/**
* Class FilterBaseController implements actions for various types
@@ -39,6 +40,45 @@ abstract class FilterBaseController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'filter';
protected static $internalModelClass = 'OPNsense\Firewall\Filter';
+ protected static $categorysource = null;
+
+ /**
+ * list categories and usage
+ * @return array
+ */
+ public function listCategoriesAction()
+ {
+ $response = ['rows' => []];
+ $catcount = [];
+ if (!empty(static::$categorysource)) {
+ $node = $this->getModel();
+ foreach (explode('.', static::$categorysource) as $ref) {
+ $node = $node->$ref;
+ }
+ foreach ($node->iterateItems() as $item) {
+ if (!empty((string)$item->categories)) {
+ foreach (explode(',', (string)$item->categories) as $cat) {
+ if (!isset($catcount[$cat])) {
+ $catcount[$cat] = 0;
+ }
+ $catcount[$cat] += 1;
+ }
+ }
+ }
+ }
+ foreach ((new Category())->categories->category->iterateItems() as $key => $category) {
+ $response['rows'][] = [
+ "uuid" => $key,
+ "name" => (string)$category->name,
+ "color" => (string)$category->color,
+ "used" => isset($catcount[$key]) ? $catcount[$key] : 0
+ ];
+ }
+ array_multisort(array_column($response['rows'], "name"), SORT_ASC, SORT_NATURAL, $response['rows']);
+
+ return $response;
+ }
+
public function applyAction($rollback_revision = null)
{
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php
index 225a07b72..7af4676eb 100644
--- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php
@@ -29,9 +29,15 @@ namespace OPNsense\Firewall\Api;
class FilterController extends FilterBaseController
{
+ protected static $categorysource = "rules.rule";
+
public function searchRuleAction()
{
- return $this->searchBase("rules.rule", array('enabled', 'sequence', 'description'), "sequence");
+ $category = $this->request->get('category');
+ $filter_funct = function ($record) use ($category) {
+ return empty($category) || array_intersect(explode(',', $record->categories), $category);
+ };
+ return $this->searchBase("rules.rule", ['enabled', 'sequence', 'description'], "sequence", $filter_funct);
}
public function setRuleAction($uuid)
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/NptController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/NptController.php
new file mode 100644
index 000000000..61b1d5705
--- /dev/null
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/NptController.php
@@ -0,0 +1,67 @@
+request->get('category');
+ $filter_funct = function ($record) use ($category) {
+ return empty($category) || array_intersect(explode(',', $record->categories), $category);
+ };
+ return $this->searchBase("npt.rule", ['enabled', 'sequence', 'description'], "sequence", $filter_funct);
+ }
+
+ public function setRuleAction($uuid)
+ {
+ return $this->setBase("rule", "npt.rule", $uuid);
+ }
+
+ public function addRuleAction()
+ {
+ return $this->addBase("rule", "npt.rule");
+ }
+
+ public function getRuleAction($uuid = null)
+ {
+ return $this->getBase("rule", "npt.rule", $uuid);
+ }
+
+ public function delRuleAction($uuid)
+ {
+ return $this->delBase("npt.rule", $uuid);
+ }
+
+ public function toggleRuleAction($uuid, $enabled = null)
+ {
+ return $this->toggleBase("npt.rule", $uuid, $enabled);
+ }
+}
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/SourceNatController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/SourceNatController.php
index 151bf14db..5833225d9 100644
--- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/SourceNatController.php
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/SourceNatController.php
@@ -29,9 +29,15 @@ namespace OPNsense\Firewall\Api;
class SourceNatController extends FilterBaseController
{
+ protected static $categorysource = "snatrules.rule";
+
public function searchRuleAction()
{
- return $this->searchBase("snatrules.rule", array('enabled', 'sequence', 'description'), "sequence");
+ $category = $this->request->get('category');
+ $filter_funct = function ($record) use ($category) {
+ return empty($category) || array_intersect(explode(',', $record->categories), $category);
+ };
+ return $this->searchBase("snatrules.rule", ['enabled', 'sequence', 'description'], "sequence", $filter_funct);
}
public function setRuleAction($uuid)
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/NptController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/NptController.php
new file mode 100644
index 000000000..07b1c7efd
--- /dev/null
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/NptController.php
@@ -0,0 +1,38 @@
+view->pick('OPNsense/Firewall/filter');
+ $this->view->ruleController = "npt";
+ $this->view->formDialogFilterRule = $this->getForm("dialogNptRule");
+ }
+}
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
index f299cc1c4..af4cda9bc 100644
--- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
@@ -99,7 +99,13 @@
checkbox
Log packets that are handled by this rule
-
+
+ rule.categories
+
+ select_multiple
+
+ For grouping purposes you may select multiple groups here to organize items.
+
rule.description
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogNptRule.xml b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogNptRule.xml
new file mode 100644
index 000000000..abfe782df
--- /dev/null
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogNptRule.xml
@@ -0,0 +1,40 @@
+
diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogSNatRule.xml b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogSNatRule.xml
index 0c87b0450..90dc7a90b 100644
--- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogSNatRule.xml
+++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogSNatRule.xml
@@ -86,6 +86,13 @@
checkbox
Log packets that are handled by this rule
+
+ rule.categories
+
+ select_multiple
+
+ For grouping purposes you may select multiple groups here to organize items.
+
rule.description
diff --git a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
index 65262b536..ca30b9f98 100644
--- a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
+++ b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
@@ -100,6 +100,17 @@
0
Y
+
+
+
+ OPNsense.Firewall.Category
+ categories.category
+ name
+
+
+ Y
+ Related category not found.
+
N
/^([\t\n\v\f\r 0-9a-zA-Z.\-,_\x{00A0}-\x{FFFF}]){0,255}$/u
@@ -184,6 +195,17 @@
0
Y
+
+
+
+ OPNsense.Firewall.Category
+ categories.category
+ name
+
+
+ Y
+ Related category not found.
+
N
/^([\t\n\v\f\r 0-9a-zA-Z.\-,_\x{00A0}-\x{FFFF}]){0,255}$/u
@@ -191,5 +213,38 @@
+
+
+
+ 1
+ Y
+
+
+ Y
+ lan
+ Y
+
+
+ Y
+
+
+
+
+
+ OPNsense.Firewall.Category
+ categories.category
+ name
+
+
+ Y
+ Related category not found.
+
+
+ N
+ /^([\t\n\v\f\r 0-9a-zA-Z.\-,_\x{00A0}-\x{FFFF}]){0,255}$/u
+ Description should be a string between 1 and 255 characters
+
+
+
diff --git a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Menu/Menu.xml b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Menu/Menu.xml
index 2889f9bff..476e1f38b 100644
--- a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Menu/Menu.xml
+++ b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Menu/Menu.xml
@@ -7,6 +7,9 @@
+
+
+
diff --git a/net/firewall/src/opnsense/mvc/app/views/OPNsense/Firewall/filter.volt b/net/firewall/src/opnsense/mvc/app/views/OPNsense/Firewall/filter.volt
index 567eff336..34622514c 100644
--- a/net/firewall/src/opnsense/mvc/app/views/OPNsense/Firewall/filter.volt
+++ b/net/firewall/src/opnsense/mvc/app/views/OPNsense/Firewall/filter.volt
@@ -2,12 +2,47 @@
$( document ).ready(function() {
let initial_load = true;
let grid = $("#grid-rules").UIBootgrid({
- search:'/api/firewall/{{ruleController}}/searchRule/',
- get:'/api/firewall/{{ruleController}}/getRule/',
- set:'/api/firewall/{{ruleController}}/setRule/',
- add:'/api/firewall/{{ruleController}}/addRule/',
- del:'/api/firewall/{{ruleController}}/delRule/',
- toggle:'/api/firewall/{{ruleController}}/toggleRule/'
+ search:'/api/firewall/{{ruleController}}/search_rule/',
+ get:'/api/firewall/{{ruleController}}/get_rule/',
+ set:'/api/firewall/{{ruleController}}/set_rule/',
+ add:'/api/firewall/{{ruleController}}/add_rule/',
+ del:'/api/firewall/{{ruleController}}/del_rule/',
+ toggle:'/api/firewall/{{ruleController}}/toggle_rule/',
+ options:{
+ requestHandler: function(request){
+ if ( $('#category_filter').val().length > 0) {
+ request['category'] = $('#category_filter').val();
+ }
+ return request;
+ }
+ }
+ });
+ grid.on("loaded.rs.jquery.bootgrid", function (e){
+ // reload categories before grid load
+ ajaxCall('/api/firewall/{{ruleController}}/list_categories', {}, function(data, status){
+ if (data.rows !== undefined) {
+ let current_selection = $("#category_filter").val();
+ $("#category_filter").empty();
+ for (i=0; i < data.rows.length ; ++i) {
+ let row = data.rows[i];
+ let opt_val = $('').html(row.name).text();
+ let bgcolor = row.color != "" ? row.color : '31708f;'; // set category color
+ let option = $("").val(row.uuid).html(row.name);
+ if (row.used > 0) {
+ option.attr(
+ 'data-content',
+ ""+opt_val + ""+
+ "" + row.used + ""
+ );
+ option.attr('id', row.uuid);
+ }
+
+ $("#category_filter").append(option);
+ }
+ $("#category_filter").val(current_selection);
+ $("#category_filter").selectpicker('refresh');
+ }
+ });
});
// open edit dialog when opened with a uuid reference
@@ -62,6 +97,11 @@
}
});
});
+ // move filter into action header
+ $("#type_filter_container").detach().prependTo('#grid-rules-header > .row > .actionBar > .actions');
+ $("#category_filter").change(function(){
+ $('#grid-rules').bootgrid('reload');
+ });
});
@@ -71,6 +111,13 @@