mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-20 03:16:12 +00:00
Firewall: NAT: One-to-One - refactor to MVC, closes https://github.com/opnsense/core/issues/7250
This commit is contained in:
parent
6f24db1a01
commit
cd81bcc964
@ -256,12 +256,6 @@ function filter_configure_sync($verbose = false, $load_aliases = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($config['nat']['onetoone'])) {
|
||||
// register user 1:1 mappings
|
||||
foreach ($config['nat']['onetoone'] as $rule) {
|
||||
$fw->registerDNatRule(500, $rule);
|
||||
}
|
||||
}
|
||||
if (!empty($config['nat']['rule'])) {
|
||||
// register user forward rules
|
||||
foreach ($config['nat']['rule'] as $rule) {
|
||||
|
||||
@ -180,6 +180,9 @@ function pf_firewall($fw)
|
||||
foreach ($mdlFilter->snatrules->rule->sortedBy(["sequence"]) as $key => $rule) {
|
||||
$fw->registerSNatRule(50, $rule->serialize());
|
||||
}
|
||||
foreach ($mdlFilter->onetoone->rule->sortedBy(["sequence"]) as $key => $rule) {
|
||||
$fw->registerDNatRule(500, $rule->serialize());
|
||||
}
|
||||
foreach ($mdlFilter->npt->rule->sortedBy(["sequence"]) as $key => $rule) {
|
||||
$fw->registerNptRule(50, $rule->serialize());
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
namespace OPNsense\Firewall\Api;
|
||||
|
||||
class OneToOneController extends FilterBaseController
|
||||
{
|
||||
protected static $categorysource = "onetoone.rule";
|
||||
|
||||
public function searchRuleAction()
|
||||
{
|
||||
$category = $this->request->get('category');
|
||||
$filter_funct = function ($record) use ($category) {
|
||||
return empty($category) || array_intersect(explode(',', $record->categories), $category);
|
||||
};
|
||||
return $this->searchBase("onetoone.rule", null, "sequence", $filter_funct);
|
||||
}
|
||||
|
||||
public function setRuleAction($uuid)
|
||||
{
|
||||
return $this->setBase("rule", "onetoone.rule", $uuid);
|
||||
}
|
||||
|
||||
public function addRuleAction()
|
||||
{
|
||||
return $this->addBase("rule", "onetoone.rule");
|
||||
}
|
||||
|
||||
public function getRuleAction($uuid = null)
|
||||
{
|
||||
return $this->getBase("rule", "onetoone.rule", $uuid);
|
||||
}
|
||||
|
||||
public function delRuleAction($uuid)
|
||||
{
|
||||
return $this->delBase("onetoone.rule", $uuid);
|
||||
}
|
||||
|
||||
public function toggleRuleAction($uuid, $enabled = null)
|
||||
{
|
||||
return $this->toggleBase("onetoone.rule", $uuid, $enabled);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
namespace OPNsense\Firewall;
|
||||
|
||||
class OneToOneController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->pick('OPNsense/Firewall/filter');
|
||||
$this->view->ruleController = "one_to_one";
|
||||
$this->view->gridFields = [
|
||||
[
|
||||
'id' => 'enabled', 'formatter' => 'rowtoggle' ,'width' => '6em', 'heading' => gettext('Enabled')
|
||||
],
|
||||
[
|
||||
'id' => 'sequence','width' => '9em', 'heading' => gettext('Sequence')
|
||||
],
|
||||
[
|
||||
'id' => 'interface','width' => '9em', 'heading' => gettext('Interface')
|
||||
],
|
||||
[
|
||||
'id' => 'target', 'heading' => gettext('External')
|
||||
],
|
||||
[
|
||||
'id' => 'source_net', 'heading' => gettext('Internal')
|
||||
],
|
||||
[
|
||||
'id' => 'destination_net', 'heading' => gettext('Destination')
|
||||
],
|
||||
[
|
||||
'id' => 'description', 'heading' => gettext('Description')
|
||||
]
|
||||
];
|
||||
|
||||
$this->view->formDialogFilterRule = $this->getForm("dialogOneToOneRule");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>rule.enabled</id>
|
||||
<label>enabled</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable this rule</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.sequence</id>
|
||||
<label>Sequence</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.log</id>
|
||||
<label>Log</label>
|
||||
<type>checkbox</type>
|
||||
<help>Log packets that are handled by this rule</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.interface</id>
|
||||
<label>Interface</label>
|
||||
<type>dropdown</type>
|
||||
<help>Choose which interface this rule applies to. Hint: in most cases, you'll want to use WAN here</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.type</id>
|
||||
<label>Type</label>
|
||||
<type>dropdown</type>
|
||||
<help>
|
||||
Select BINAT (default) or NAT here, when nets are equally sized binat is usually the best option.Using NAT we can also map unequal sized networks.
|
||||
A BINAT rule specifies a bidirectional mapping between an external and internal network and can be used from both ends, nat only applies in one direction.
|
||||
</help>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<id>rule.external</id>
|
||||
<label>External network (Target)</label>
|
||||
<type>text</type>
|
||||
<help>Enter the external subnet's starting address for the 1:1 mapping or network. This is the address or network the traffic will translate to/from.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.source_not</id>
|
||||
<label>Source / Invert</label>
|
||||
<type>checkbox</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Use this option to invert the sense of the match.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.source_net</id>
|
||||
<label>Source / Internal</label>
|
||||
<type>text</type>
|
||||
<style>net_selector</style>
|
||||
<help>Enter the internal subnet for the 1:1 mapping.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.destination_not</id>
|
||||
<label>Destination / Invert</label>
|
||||
<type>checkbox</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Use this option to invert the sense of the match.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.destination_net</id>
|
||||
<label>Destination</label>
|
||||
<type>text</type>
|
||||
<style>net_selector</style>
|
||||
<help>The 1:1 mapping will only be used for connections to or from the specified destination. Hint: this is usually 'any'.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.categories</id>
|
||||
<label>Categories</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<help>For grouping purposes you may select multiple groups here to organize items.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>rule.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
</form>
|
||||
@ -238,18 +238,6 @@
|
||||
<pattern>api/firewall/category/*</pattern>
|
||||
</patterns>
|
||||
</page-firewall-categories>
|
||||
<page-firewall-nat-1-1>
|
||||
<name>Firewall: NAT: 1:1</name>
|
||||
<patterns>
|
||||
<pattern>firewall_nat_1to1.php*</pattern>
|
||||
</patterns>
|
||||
</page-firewall-nat-1-1>
|
||||
<page-firewall-nat-1-1-edit>
|
||||
<name>Firewall: NAT: 1:1: Edit</name>
|
||||
<patterns>
|
||||
<pattern>firewall_nat_1to1_edit.php*</pattern>
|
||||
</patterns>
|
||||
</page-firewall-nat-1-1-edit>
|
||||
<page-firewall-nat-outbound>
|
||||
<name>Firewall: NAT: Outbound</name>
|
||||
<patterns>
|
||||
|
||||
@ -133,9 +133,6 @@
|
||||
<PortForward order="100" VisibleName="Port Forward" url="/firewall_nat.php">
|
||||
<Edit url="/firewall_nat_edit.php*" visibility="hidden"/>
|
||||
</PortForward>
|
||||
<OneToOne order="200" VisibleName="One-to-One" url="/firewall_nat_1to1.php">
|
||||
<Edit url="/firewall_nat_1to1_edit.php*" visibility="hidden"/>
|
||||
</OneToOne>
|
||||
<Outbound order="300" VisibleName="Outbound" url="/firewall_nat_out.php">
|
||||
<Edit url="/firewall_nat_out_edit.php*" visibility="hidden"/>
|
||||
</Outbound>
|
||||
|
||||
@ -20,4 +20,11 @@
|
||||
<pattern>api/firewall/npt/*</pattern>
|
||||
</patterns>
|
||||
</page-firewall-nat-npt>
|
||||
<page-firewall-nat-1-1-edit>
|
||||
<name>Firewall: NAT: 1:1</name>
|
||||
<patterns>
|
||||
<pattern>ui/firewall/one_to_one/*</pattern>
|
||||
<pattern>api/firewall/one_to_one/*</pattern>
|
||||
</patterns>
|
||||
</page-firewall-nat-1-1-edit>
|
||||
</acl>
|
||||
|
||||
@ -134,6 +134,44 @@ class Filter extends BaseModel
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 1 to 1 mappings */
|
||||
foreach ($this->onetoone->rule->iterateItems() as $rule) {
|
||||
if ($validateFullModel || $rule->isFieldChanged()) {
|
||||
$ipprotos = [];
|
||||
$subnets = [];
|
||||
foreach (['source_net', 'destination_net', 'external'] as $fieldname) {
|
||||
$subnets[$fieldname] = null;
|
||||
if (Util::isSubnet($rule->$fieldname) || Util::isIpAddress($rule->$fieldname)) {
|
||||
$ipprotos[$fieldname] = strpos($rule->$fieldname, ':') === false ? "inet" : "inet6";
|
||||
$subnet_default = $ipprotos[$fieldname] == 'inet' ? '32' : '128';
|
||||
$subnets[$fieldname] = explode('/', $rule->$fieldname . '/'.$subnet_default)[1];
|
||||
}
|
||||
}
|
||||
if (count(array_unique(array_values($ipprotos))) > 1) {
|
||||
foreach (array_keys($ipprotos) as $fieldname) {
|
||||
$messages->appendMessage(new Message(
|
||||
gettext("IP protocol families should match."),
|
||||
$rule->$fieldname->__reference
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($rule->type == 'binat' && !empty((string)$rule->enabled)) {
|
||||
/* binat rules are more strict, when not enabled, we may skip the validations to ease migration */
|
||||
if (empty($ipprotos['source_net'])) {
|
||||
$messages->appendMessage(new Message(
|
||||
gettext("For BINAT rules only addresses or subnets are allowed."),
|
||||
$rule->source_net->__reference
|
||||
));
|
||||
} elseif ($subnets['external'] != $subnets['source_net']) {
|
||||
$messages->appendMessage(new Message(
|
||||
gettext("External subnet should match internal subnet."),
|
||||
$rule->external->__reference
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<model>
|
||||
<mount>//OPNsense/Firewall/Filter</mount>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.4</version>
|
||||
<migration_prefix>MFP</migration_prefix>
|
||||
<description>OPNsense firewall filter rules</description>
|
||||
<items>
|
||||
@ -245,5 +245,75 @@
|
||||
<description type="DescriptionField"/>
|
||||
</rule>
|
||||
</npt>
|
||||
<onetoone>
|
||||
<rule type=".\SourceNatRuleField">
|
||||
<enabled type="BooleanField">
|
||||
<Default>1</Default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<log type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</log>
|
||||
<sequence type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>99999</MaximumValue>
|
||||
<ValidationMessage>provide a valid sequence for sorting</ValidationMessage>
|
||||
<Required>Y</Required>
|
||||
<Default>1</Default>
|
||||
</sequence>
|
||||
<interface type="InterfaceField">
|
||||
<Required>Y</Required>
|
||||
<Default>wan</Default>
|
||||
<AllowDynamic>Y</AllowDynamic>
|
||||
</interface>
|
||||
<type type="OptionField">
|
||||
<Default>binat</Default>
|
||||
<Required>Y</Required>
|
||||
<OptionValues>
|
||||
<binat>BINAT</binat>
|
||||
<nat>NAT</nat>
|
||||
</OptionValues>
|
||||
</type>
|
||||
<source_net type="NetworkAliasField">
|
||||
<Required>Y</Required>
|
||||
</source_net>
|
||||
<source_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</source_not>
|
||||
<destination_net type="NetworkAliasField">
|
||||
<Required>Y</Required>
|
||||
<Default>any</Default>
|
||||
</destination_net>
|
||||
<destination_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</destination_not>
|
||||
<external type="NetworkField">
|
||||
<Required>Y</Required>
|
||||
<WildcardEnabled>N</WildcardEnabled>
|
||||
</external>
|
||||
<natreflection type="OptionField">
|
||||
<OptionValues>
|
||||
<default value=''>default</default>
|
||||
<enable>Enable</enable>
|
||||
<disable>Disable</disable>
|
||||
</OptionValues>
|
||||
</natreflection>
|
||||
<categories type="ModelRelationField">
|
||||
<Model>
|
||||
<rulesets>
|
||||
<source>OPNsense.Firewall.Category</source>
|
||||
<items>categories.category</items>
|
||||
<display>name</display>
|
||||
</rulesets>
|
||||
</Model>
|
||||
<Multiple>Y</Multiple>
|
||||
<ValidationMessage>Related category not found.</ValidationMessage>
|
||||
</categories>
|
||||
<description type="DescriptionField"/>
|
||||
</rule>
|
||||
</onetoone>
|
||||
</items>
|
||||
</model>
|
||||
|
||||
@ -9,6 +9,9 @@
|
||||
</SourceNat>
|
||||
</Automation>
|
||||
<NAT>
|
||||
<OneToOne order="200" VisibleName="One-to-One" url="/ui/firewall/one_to_one/">
|
||||
<FilterRef url="/ui/firewall/one_to_one#*" visibility="hidden"/>
|
||||
</OneToOne>
|
||||
<NPTv6 order="400" url="/ui/firewall/npt/">
|
||||
<FilterRef url="/ui/firewall/npt#*" visibility="hidden"/>
|
||||
</NPTv6>
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2024 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.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OPNsense\Firewall\Migrations;
|
||||
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Base\BaseModelMigration;
|
||||
use OPNsense\Firewall\Filter;
|
||||
use OPNsense\Firewall\Category;
|
||||
use OPNsense\Firewall\Util;
|
||||
|
||||
// <!-- external, category, descr, interface, type, src, dst, natreflection, disabled -->
|
||||
|
||||
class MFP1_0_4 extends BaseModelMigration
|
||||
{
|
||||
public function run($model)
|
||||
{
|
||||
if ($model instanceof Filter) {
|
||||
$sequence = 1;
|
||||
$catmdl = new Category();
|
||||
foreach ((Config::getInstance()->object())->nat->children() as $child) {
|
||||
if ($child->getName() == 'onetoone') {
|
||||
$addr = [] ;
|
||||
foreach (['destination', 'source'] as $fieldname) {
|
||||
if (!empty(((string)$child->$fieldname->any))) {
|
||||
$addr[$fieldname] = 'any';
|
||||
} elseif (Util::isSubnet((string)$child->$fieldname->address)) {
|
||||
$addr[$fieldname] = (string)$child->$fieldname->address;
|
||||
} elseif (Util::isIpAddress((string)$child->$fieldname->address)) {
|
||||
$subn = strpos($child->$fieldname->address, ':') === false ? '32' : '128';
|
||||
$addr[$fieldname] = (string)$child->$fieldname->address . '/' . $subn;
|
||||
} elseif (!empty((string)$child->$fieldname->address)) {
|
||||
$addr[$fieldname] = (string)$child->$fieldname->address;
|
||||
} elseif (!empty((string)$child->$fieldname->network)) {
|
||||
$addr[$fieldname] = (string)$child->$fieldname->network;
|
||||
} else {
|
||||
$addr[$fieldname] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($addr['source']) && !empty($addr['destination']) && !empty((string)$child->external)) {
|
||||
$node = $model->onetoone->rule->Add();
|
||||
$node->enabled = empty((string)$child->disabled) ? "1" : "0";
|
||||
$node->log = empty((string)$child->log) ? "0" : "1";
|
||||
$node->sequence = (string)($sequence++);
|
||||
if (!empty((string)$child->category)) {
|
||||
$cats = [];
|
||||
foreach (explode(',', (string)$child->category) as $cat) {
|
||||
$tmp = $catmdl->getByName($cat);
|
||||
if ($tmp != null) {
|
||||
$cats[] = $tmp->getAttributes()['uuid'];
|
||||
}
|
||||
}
|
||||
$node->categories = implode(",", $cats);
|
||||
}
|
||||
$node->interface = (string)$child->interface;
|
||||
$node->type = !empty((string)$child->type) ? (string)$child->type : 'binat';
|
||||
$node->external = (string)$child->external;
|
||||
$node->source_net = $addr['source'];
|
||||
$node->destination_net = $addr['source'];
|
||||
$node->source_not = !empty((string)$child->source->not) ? '1' : '0';
|
||||
$node->destination_not = !empty((string)$child->destination->not) ? '1' : '0';
|
||||
|
||||
$node->description = (string)$child->descr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function post($model)
|
||||
{
|
||||
if ($model instanceof Filter) {
|
||||
$cfgObj = Config::getInstance()->object();
|
||||
if (isset($cfgObj->nat->onetoone)) {
|
||||
unset($cfgObj->nat->onetoone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,410 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Deciso B.V.
|
||||
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require_once("guiconfig.inc");
|
||||
require_once("interfaces.inc");
|
||||
require_once("filter.inc");
|
||||
|
||||
$a_1to1 = &config_read_array('nat', 'onetoone');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$pconfig = $_POST;
|
||||
if (isset($pconfig['id']) && isset($a_1to1[$pconfig['id']])) {
|
||||
// id found and valid
|
||||
$id = $pconfig['id'];
|
||||
}
|
||||
|
||||
if (isset($pconfig['apply'])) {
|
||||
filter_configure();
|
||||
$savemsg = get_std_save_message();
|
||||
clear_subsystem_dirty('natconf');
|
||||
clear_subsystem_dirty('filter');
|
||||
} elseif (isset($pconfig['action']) && $pconfig['action'] == 'del' && isset($id)) {
|
||||
// delete single entry
|
||||
unset($a_1to1[$id]);
|
||||
write_config();
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
} elseif (isset($pconfig['action']) && $pconfig['action'] == 'del_x' && isset($pconfig['rule']) && count($pconfig['rule']) > 0) {
|
||||
// delete selected
|
||||
foreach ($pconfig['rule'] as $rulei) {
|
||||
unset($a_1to1[$rulei]);
|
||||
}
|
||||
write_config();
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
} elseif (isset($pconfig['action']) && in_array($pconfig['action'], array('toggle_enable', 'toggle_disable')) && isset($pconfig['rule']) && count($pconfig['rule']) > 0) {
|
||||
foreach ($pconfig['rule'] as $rulei) {
|
||||
$a_1to1[$rulei]['disabled'] = $pconfig['action'] == 'toggle_disable';
|
||||
}
|
||||
write_config();
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
} elseif (isset($pconfig['action']) && $pconfig['action'] == 'move') {
|
||||
// move selected
|
||||
if (isset($pconfig['rule']) && count($pconfig['rule']) > 0) {
|
||||
// if rule not set/found, move to end
|
||||
if (!isset($id)) {
|
||||
$id = count($a_1to1);
|
||||
}
|
||||
$a_1to1 = legacy_move_config_list_items($a_1to1, $id, $pconfig['rule']);
|
||||
|
||||
write_config();
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
}
|
||||
} elseif (isset($pconfig['action']) && $pconfig['action'] == 'toggle' && isset($id)) {
|
||||
// toggle item
|
||||
if(isset($a_1to1[$id]['disabled'])) {
|
||||
unset($a_1to1[$id]['disabled']);
|
||||
} else {
|
||||
$a_1to1[$id]['disabled'] = true;
|
||||
}
|
||||
write_config('Toggled NAT 1:1 rule');
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
legacy_html_escape_form_data($a_1to1);
|
||||
|
||||
include("head.inc");
|
||||
|
||||
?>
|
||||
<body>
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
// link delete buttons
|
||||
$(".act_delete").click(function(event){
|
||||
event.preventDefault();
|
||||
var id = $(this).attr("id").split('_').pop(-1);
|
||||
if (id != 'x') {
|
||||
// delete single
|
||||
BootstrapDialog.show({
|
||||
type:BootstrapDialog.TYPE_DANGER,
|
||||
title: "<?= gettext("1:1");?>",
|
||||
message: "<?=gettext("Do you really want to delete this rule?");?>",
|
||||
buttons: [{
|
||||
label: "<?= gettext("No");?>",
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}}, {
|
||||
label: "<?= gettext("Yes");?>",
|
||||
action: function(dialogRef) {
|
||||
$("#id").val(id);
|
||||
$("#action").val("del");
|
||||
$("#iform").submit()
|
||||
}
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
// delete selected
|
||||
BootstrapDialog.show({
|
||||
type:BootstrapDialog.TYPE_DANGER,
|
||||
title: "<?= gettext("1:1");?>",
|
||||
message: "<?=gettext("Do you really want to delete the selected rules?");?>",
|
||||
buttons: [{
|
||||
label: "<?= gettext("No");?>",
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}}, {
|
||||
label: "<?= gettext("Yes");?>",
|
||||
action: function(dialogRef) {
|
||||
$("#id").val("");
|
||||
$("#action").val("del_x");
|
||||
$("#iform").submit()
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// enable/disable selected
|
||||
$(".act_toggle_enable").click(function(event){
|
||||
event.preventDefault();
|
||||
BootstrapDialog.show({
|
||||
type:BootstrapDialog.TYPE_DANGER,
|
||||
title: "<?= gettext("Rules");?>",
|
||||
message: "<?=gettext("Enable selected rules?");?>",
|
||||
buttons: [{
|
||||
label: "<?= gettext("No");?>",
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}}, {
|
||||
label: "<?= gettext("Yes");?>",
|
||||
action: function(dialogRef) {
|
||||
$("#id").val("");
|
||||
$("#action").val("toggle_enable");
|
||||
$("#iform").submit()
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
$(".act_toggle_disable").click(function(event){
|
||||
event.preventDefault();
|
||||
BootstrapDialog.show({
|
||||
type:BootstrapDialog.TYPE_DANGER,
|
||||
title: "<?= gettext("Rules");?>",
|
||||
message: "<?=gettext("Disable selected rules?");?>",
|
||||
buttons: [{
|
||||
label: "<?= gettext("No");?>",
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}}, {
|
||||
label: "<?= gettext("Yes");?>",
|
||||
action: function(dialogRef) {
|
||||
$("#id").val("");
|
||||
$("#action").val("toggle_disable");
|
||||
$("#iform").submit()
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
// link move buttons
|
||||
$(".act_move").click(function(event){
|
||||
event.preventDefault();
|
||||
var id = $(this).attr("id").split('_').pop(-1);
|
||||
$("#id").val(id);
|
||||
$("#action").val("move");
|
||||
$("#iform").submit();
|
||||
});
|
||||
|
||||
// link toggle buttons
|
||||
$(".act_toggle").click(function(event){
|
||||
event.preventDefault();
|
||||
var id = $(this).attr("id").split('_').pop(-1);
|
||||
$("#id").val(id);
|
||||
$("#action").val("toggle");
|
||||
$("#iform").submit();
|
||||
});
|
||||
// select All
|
||||
$("#selectAll").click(function(){
|
||||
$(".rule_select:not(:disabled)").prop("checked", $(this).prop("checked"));
|
||||
});
|
||||
|
||||
// watch scroll position and set to last known on page load
|
||||
watchScrollPosition();
|
||||
|
||||
// move category block
|
||||
$("#category_block").detach().appendTo($(".page-content-head > .container-fluid > .list-inline"));
|
||||
$("#category_block").addClass("pull-right");
|
||||
|
||||
// our usual zebra striping doesn't respect hidden rows, hook repaint on .opnsense-rules change() and fire initially
|
||||
$(".opnsense-rules > tbody > tr").each(function(){
|
||||
// save zebra color
|
||||
let tr_color = $(this).children(0).css("background-color");
|
||||
if (tr_color != 'transparent' && !tr_color.includes('(0, 0, 0')) {
|
||||
$("#fw_category").data('stripe_color', tr_color);
|
||||
}
|
||||
});
|
||||
$(".opnsense-rules").removeClass("table-striped");
|
||||
$(".opnsense-rules").change(function(){
|
||||
$(".opnsense-rules > tbody > tr:visible").each(function (index) {
|
||||
$(this).css("background-color", "inherit");
|
||||
if ( index % 2 == 0) {
|
||||
$(this).css("background-color", $("#fw_category").data('stripe_color'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// hook category functionality
|
||||
hook_firewall_categories();
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<?php include("fbegin.inc"); ?>
|
||||
<div class="hidden">
|
||||
<div id="category_block" style="z-index:-100;">
|
||||
<select class="selectpicker hidden-xs hidden-sm hidden-md" data-live-search="true" data-size="5" multiple title="<?=gettext("Select category");?>" id="fw_category">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php
|
||||
print_service_banner('firewall');
|
||||
if (isset($savemsg))
|
||||
print_info_box($savemsg);
|
||||
if (is_subsystem_dirty('natconf'))
|
||||
print_info_box_apply(gettext("The NAT configuration has been changed.") .
|
||||
"<br />" .
|
||||
gettext("You must apply the changes in order for them to take effect."));
|
||||
?>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<form method="post" name="iform" id="iform">
|
||||
<input type="hidden" id="id" name="id" value="" />
|
||||
<input type="hidden" id="action" name="action" value="" />
|
||||
<table class="table table-striped table-condensed opnsense-rules">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"></th>
|
||||
<th> </th>
|
||||
<th><?=gettext("Interface"); ?></th>
|
||||
<th><?=gettext("External IP"); ?></th>
|
||||
<th><?=gettext("Internal IP"); ?></th>
|
||||
<th><?=gettext("Destination IP"); ?></th>
|
||||
<th><?=gettext("Description"); ?></th>
|
||||
<th class="text-nowrap">
|
||||
<a href="firewall_nat_1to1_edit.php" class="btn btn-primary btn-xs" data-toggle="tooltip" title="<?= html_safe(gettext('Add')) ?>">
|
||||
<i class="fa fa-plus fa-fw"></i>
|
||||
</a>
|
||||
<?php if (count($a_1to1)): ?>
|
||||
<button id="move_<?= count($a_1to1) ?>" name="move_<?= count($a_1to1) ?>_x" data-toggle="tooltip" title="<?= html_safe(gettext('Move selected rules to end')) ?>" class="act_move btn btn-default btn-xs">
|
||||
<i class="fa fa-arrow-left fa-fw"></i>
|
||||
</button>
|
||||
<button id="del_x" title="<?= html_safe(gettext('Delete selected')) ?>" data-toggle="tooltip" class="act_delete btn btn-default btn-xs">
|
||||
<i class="fa fa-trash fa-fw"></i>
|
||||
</button>
|
||||
<button title="<?= html_safe(gettext('Enable selected')) ?>" data-toggle="tooltip" class="act_toggle_enable btn btn-default btn-xs">
|
||||
<i class="fa fa-check-square-o fa-fw"></i>
|
||||
</button>
|
||||
<button title="<?= html_safe(gettext('Disable selected')) ?>" data-toggle="tooltip" class="act_toggle_disable btn btn-default btn-xs">
|
||||
<i class="fa fa-square-o fa-fw"></i>
|
||||
</button>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$i = 0;
|
||||
foreach ($a_1to1 as $natent):
|
||||
?>
|
||||
<tr class="rule <?=isset($natent['disabled'])?"text-muted":"";?>" data-category="<?=!empty($natent['category']) ? $natent['category'] : "";?>" ondblclick="document.location='firewall_nat_1to1_edit.php?id=<?=$i;?>';">
|
||||
<td>
|
||||
<input class="rule_select" type="checkbox" name="rule[]" value="<?=$i;?>" />
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" type="submit" id="toggle_<?=$i;?>" data-toggle="tooltip" title="<?=(!isset($natent['disabled'])) ? gettext("Disable") : gettext("Enable");?>" class="act_toggle">
|
||||
<?php if(isset($natent['disabled'])):?>
|
||||
<span class="fa fa-play text-muted"></span>
|
||||
<?php else:?>
|
||||
<span class="fa fa-play text-success"></span>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<?=htmlspecialchars(convert_friendly_interface_to_friendly_descr(isset($natent['interface']) ? $natent['interface'] : "wan"));?>
|
||||
</td>
|
||||
<td>
|
||||
<?=isset($natent['external']) ? $natent['external'] : "";?><?=isset($natent['source']) && strpos($natent['external'], '/') === false ? strstr(pprint_address($natent['source']), '/') : "";?>
|
||||
<?php if (isset($natent['external']['address']) && is_alias($natent['external']['address'])): ?>
|
||||
<a href="/ui/firewall/alias/index/<?=htmlspecialchars($natent['external']['address']);?>"><i class="fa fa-list"></i> </a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($natent['source']['address']) && is_alias($natent['source']['address'])): ?>
|
||||
<span title="<?=htmlspecialchars(get_alias_description($natent['source']['address']));?>" data-toggle="tooltip" data-html="true">
|
||||
<?=htmlspecialchars(pprint_address($natent['source']));?>
|
||||
</span>
|
||||
<a href="/ui/firewall/alias/index/<?=htmlspecialchars($natent['source']['address']);?>"
|
||||
title="<?=gettext("edit alias");?>" data-toggle="tooltip">
|
||||
<i class="fa fa-list"></i>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<?=htmlspecialchars(pprint_address($natent['source']));?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($natent['destination']['address']) && is_alias($natent['destination']['address'])): ?>
|
||||
<span title="<?=htmlspecialchars(get_alias_description($natent['destination']['address']));?>" data-toggle="tooltip" data-html="true">
|
||||
<?=htmlspecialchars(pprint_address($natent['destination']));?>
|
||||
</span>
|
||||
<a href="/ui/firewall/alias/index/<?=htmlspecialchars($natent['destination']['address']);?>"
|
||||
title="<?=gettext("edit alias");?>" data-toggle="tooltip">
|
||||
<i class="fa fa-list"></i>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<?=htmlspecialchars(pprint_address($natent['destination']));?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="rule-description">
|
||||
<?=$natent['descr'];?>
|
||||
</td>
|
||||
<td>
|
||||
<a type="submit" id="move_<?=$i;?>" name="move_<?=$i;?>_x" data-toggle="tooltip" title="<?= html_safe(gettext("Move selected rules before this rule")) ?>" class="act_move btn btn-default btn-xs">
|
||||
<span class="fa fa-arrow-left fa-fw"></span>
|
||||
</a>
|
||||
<a href="firewall_nat_1to1_edit.php?id=<?=$i;?>" class="btn btn-default btn-xs" data-toggle="tooltip" title="<?= html_safe(gettext('Edit')) ?>">
|
||||
<span class="fa fa-pencil fa-fw"></span>
|
||||
</a>
|
||||
<a id="del_<?=$i;?>" title="<?= html_safe(gettext('Delete')) ?>" data-toggle="tooltip" class="act_delete btn btn-default btn-xs">
|
||||
<span class="fa fa-trash fa-fw"></span>
|
||||
</a>
|
||||
<a href="firewall_nat_1to1_edit.php?dup=<?=$i;?>" data-toggle="tooltip" title="<?= html_safe(gettext('Clone')) ?>" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-clone fa-fw"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
$i++;
|
||||
endforeach;
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td style="width:16px"><span class="fa fa-play text-success"></span></td>
|
||||
<td colspan="8"><?=gettext("Enabled rule"); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="fa fa-play text-muted"></span></td>
|
||||
<td colspan="8"><?=gettext("Disabled rule"); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a><i class="fa fa-list"></i></a></td>
|
||||
<td colspan="8"><?=gettext("Alias (click to view/edit)");?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="9">
|
||||
<?=gettext("If you add a 1:1 NAT entry for any of the interface IPs on this system, " .
|
||||
"it will make this system inaccessible on that IP address. i.e. if " .
|
||||
"you use your WAN IP address, any services on this system (IPsec, OpenVPN server, etc.) " .
|
||||
"using the WAN IP address will no longer function."); ?>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php include("foot.inc"); ?>
|
||||
@ -1,475 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Deciso B.V.
|
||||
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require_once("guiconfig.inc");
|
||||
require_once("interfaces.inc");
|
||||
|
||||
$a_1to1 = &config_read_array('nat', 'onetoone');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// input record id, if valid
|
||||
if (isset($_GET['dup']) && isset($a_1to1[$_GET['dup']])) {
|
||||
$configId = $_GET['dup'];
|
||||
} elseif (isset($_GET['id']) && isset($a_1to1[$_GET['id']])) {
|
||||
$id = $_GET['id'];
|
||||
$configId = $id;
|
||||
}
|
||||
|
||||
$pconfig = array();
|
||||
// set defaults
|
||||
$pconfig['interface'] = "wan";
|
||||
$pconfig['src'] = 'lan';
|
||||
$pconfig['dst'] = 'any';
|
||||
$pconfig['type'] = 'binat';
|
||||
if (isset($configId)) {
|
||||
// copy settings from config
|
||||
foreach (array('disabled','interface','external','descr','natreflection', 'type', 'category') as $fieldname) {
|
||||
if (isset($a_1to1[$configId][$fieldname])) {
|
||||
$pconfig[$fieldname] = $a_1to1[$configId][$fieldname];
|
||||
} else {
|
||||
$pconfig[$fieldname] = null;
|
||||
}
|
||||
}
|
||||
// read settings with some kind of logic
|
||||
address_to_pconfig(
|
||||
$a_1to1[$configId]['source'], $pconfig['src'],
|
||||
$pconfig['srcmask'], $pconfig['srcnot'],
|
||||
$pconfig['__unused__'],$pconfig['__unused__']
|
||||
);
|
||||
|
||||
address_to_pconfig(
|
||||
$a_1to1[$configId]['destination'], $pconfig['dst'],
|
||||
$pconfig['dstmask'], $pconfig['dstnot'],
|
||||
$pconfig['__unused__'],$pconfig['__unused__']
|
||||
);
|
||||
} else {
|
||||
// init form data on new
|
||||
foreach (array('disabled','interface','external','descr','natreflection'
|
||||
,'src','srcmask','srcnot','dst','dstmask','dstnot'
|
||||
) as $fieldname) {
|
||||
if (!isset($pconfig[$fieldname])) {
|
||||
$pconfig[$fieldname] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$pconfig['category'] = !empty($pconfig['category']) ? explode(",", $pconfig['category']) : [];
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input_errors = array();
|
||||
$pconfig = $_POST;
|
||||
// input record id, if valid
|
||||
if (isset($_POST['id']) && isset($a_1to1[$_POST['id']])) {
|
||||
$id = $_POST['id'];
|
||||
}
|
||||
|
||||
// trim input
|
||||
foreach (array('external','src','dst') as $fieldname) {
|
||||
if (isset($pconfig[$fieldname])) {
|
||||
$pconfig[$fieldname] = trim($pconfig[$fieldname]);
|
||||
}
|
||||
}
|
||||
|
||||
/* input validation */
|
||||
$reqdfields = explode(" ", "interface external src dst");
|
||||
$reqdfieldsn = array(gettext("Interface"), gettext("External subnet"), gettext("Source address"), gettext("Destination address"));
|
||||
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
|
||||
|
||||
|
||||
/* For external, user can enter only ip's */
|
||||
$tmpext = explode('/', $pconfig['external']);
|
||||
if (!empty($pconfig['external'])) {
|
||||
if ($pconfig['type'] == 'binat' && (!is_ipaddr($tmpext[0]) || (count($tmpext) != 1 && $pconfig['srcmask'] != $tmpext[1]))) {
|
||||
$input_errors[] = gettext("A valid external subnet must be specified.");
|
||||
} elseif ($pconfig['type'] == 'nat' && !is_subnet($pconfig['external'])) {
|
||||
$input_errors[] = gettext("A valid external subnet must be specified.");
|
||||
}
|
||||
}
|
||||
/* For src, user can enter only ip's or networks */
|
||||
if ($pconfig['type'] == 'binat' && !is_subnet($pconfig['src']) && !is_ipaddr($pconfig['src'])) {
|
||||
$input_errors[] = sprintf(gettext("%s is not a valid source IP address."), $pconfig['src']);
|
||||
} elseif (!is_specialnet($pconfig['src']) && !is_ipaddroralias($pconfig['src'])) {
|
||||
$input_errors[] = sprintf(gettext("%s is not a valid source IP address or alias."), $pconfig['src']);
|
||||
}
|
||||
if (!empty($pconfig['srcmask']) && !is_numericint($pconfig['srcmask'])) {
|
||||
$input_errors[] = gettext("A valid source bit count must be specified.");
|
||||
}
|
||||
/* For dst, user can enter ip's, networks or aliases */
|
||||
if (!is_specialnet($pconfig['dst']) && !is_ipaddroralias($pconfig['dst'])) {
|
||||
$input_errors[] = sprintf(gettext("%s is not a valid destination IP address or alias."), $pconfig['dst']);
|
||||
}
|
||||
if (!empty($pconfig['dstmask']) && !is_numericint($pconfig['dstmask'])) {
|
||||
$input_errors[] = gettext("A valid destination bit count must be specified.");
|
||||
}
|
||||
|
||||
if (count($input_errors) == 0) {
|
||||
$natent = array();
|
||||
// 1-on-1 copy
|
||||
$natent['external'] = $pconfig['external'];
|
||||
$natent['category'] = !empty($pconfig['category']) ? implode(",", $pconfig['category']) : null;
|
||||
$natent['descr'] = $pconfig['descr'];
|
||||
$natent['interface'] = $pconfig['interface'];
|
||||
$natent['type'] = $pconfig['type'];
|
||||
|
||||
// copy form data with some kind of logic in it
|
||||
$natent['disabled'] = isset($_POST['disabled']) ? true:false;
|
||||
pconfig_to_address($natent['source'], $pconfig['src'],
|
||||
$pconfig['srcmask'], !empty($pconfig['srcnot']));
|
||||
|
||||
pconfig_to_address($natent['destination'], $pconfig['dst'],
|
||||
$pconfig['dstmask'], !empty($pconfig['dstnot']));
|
||||
|
||||
if (isset($pconfig['natreflection'] ) && ($pconfig['natreflection'] == "enable" || $pconfig['natreflection'] == "disable")) {
|
||||
$natent['natreflection'] = $pconfig['natreflection'];
|
||||
}
|
||||
|
||||
// save data
|
||||
if (isset($id)) {
|
||||
$a_1to1[$id] = $natent;
|
||||
} else {
|
||||
$a_1to1[] = $natent;
|
||||
}
|
||||
|
||||
OPNsense\Core\Config::getInstance()->fromArray($config);
|
||||
$catmdl = new OPNsense\Firewall\Category();
|
||||
if ($catmdl->sync()) {
|
||||
$catmdl->serializeToConfig();
|
||||
$config = OPNsense\Core\Config::getInstance()->toArray(listtags());
|
||||
}
|
||||
write_config();
|
||||
mark_subsystem_dirty('natconf');
|
||||
header(url_safe('Location: /firewall_nat_1to1.php'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
legacy_html_escape_form_data($pconfig);
|
||||
|
||||
include("head.inc");
|
||||
|
||||
?>
|
||||
|
||||
<body>
|
||||
<script src="<?= cache_safe('/ui/js/tokenize2.js') ?>"></script>
|
||||
<link rel="stylesheet" type="text/css" href="<?= cache_safe(get_themed_filename('/css/tokenize2.css')) ?>">
|
||||
<script src="<?= cache_safe('/ui/js/opnsense_ui.js') ?>"></script>
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
|
||||
// select / input combination, link behaviour
|
||||
// when the data attribute "data-other" is selected, display related input item(s)
|
||||
// push changes from input back to selected option value
|
||||
$('[for!=""][for]').each(function(){
|
||||
var refObj = $("#"+$(this).attr("for"));
|
||||
if (refObj.is("select")) {
|
||||
// connect on change event to select box (show/hide)
|
||||
refObj.change(function(){
|
||||
if ($(this).find(":selected").attr("data-other") == "true") {
|
||||
// show related controls
|
||||
$('*[for="'+$(this).attr("id")+'"]').each(function(){
|
||||
if ($(this).hasClass("selectpicker")) {
|
||||
$(this).selectpicker('show');
|
||||
} else {
|
||||
$(this).removeClass("hidden");
|
||||
}
|
||||
$(this).prop("disabled", false);
|
||||
});
|
||||
} else {
|
||||
// hide related controls
|
||||
$('*[for="'+$(this).attr("id")+'"]').each(function(){
|
||||
if ($(this).hasClass("selectpicker")) {
|
||||
$(this).selectpicker('hide');
|
||||
} else {
|
||||
$(this).addClass("hidden");
|
||||
}
|
||||
$(this).prop("disabled", true);
|
||||
});
|
||||
}
|
||||
});
|
||||
// update initial
|
||||
refObj.change();
|
||||
|
||||
// connect on change to input to save data to selector
|
||||
if ($(this).attr("name") == undefined) {
|
||||
$(this).change(function(){
|
||||
var otherOpt = $('#'+$(this).attr('for')+' > option[data-other="true"]') ;
|
||||
otherOpt.attr("value",$(this).val());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// aliases and "special nets" are only allowed for nat type entries
|
||||
$("#nattype").change(function(){
|
||||
if ($(this).val() == 'binat') {
|
||||
$("#src optgroup[data-type='nat']").children().prop('disabled', true);
|
||||
} else {
|
||||
$("#src optgroup[data-type='nat']").children().prop('disabled', false);
|
||||
}
|
||||
$("#src").selectpicker('refresh');
|
||||
});
|
||||
$("#nattype").change();
|
||||
formatTokenizersUI();
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include("fbegin.inc"); ?>
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php
|
||||
if (isset($input_errors) && count($input_errors) > 0)
|
||||
print_input_errors($input_errors);
|
||||
?>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<form method="post" name="iform" id="iform">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped opnsense_standard_table_form">
|
||||
<tr>
|
||||
<td style="width:22%"><strong><?= gettext('Edit NAT 1:1 entry') ?></strong></td>
|
||||
<td style="width:78%;text-align:right">
|
||||
<small><?=gettext("full help"); ?> </small>
|
||||
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_disabled" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Disabled"); ?></td>
|
||||
<td>
|
||||
<input name="disabled" type="checkbox" id="disabled" value="yes" <?= !empty($pconfig['disabled']) ? "checked=\"checked\"" : ""; ?> />
|
||||
<div class="hidden" data-for="help_for_disabled">
|
||||
<strong><?=gettext("Disable this rule"); ?></strong><br />
|
||||
<?=gettext("Set this option to disable this rule without removing it from the list."); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_interface" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Interface"); ?></td>
|
||||
<td>
|
||||
<select name="interface" class="selectpicker" data-width="348px" data-live-search="true">
|
||||
<?php
|
||||
foreach (legacy_config_get_interfaces(array("enable" => true)) as $iface => $ifdetail): ?>
|
||||
<option value="<?=$iface;?>" <?= $iface == $pconfig['interface'] ? "selected=\"selected\"" : ""; ?>>
|
||||
<?=htmlspecialchars($ifdetail['descr']);?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_interface">
|
||||
<?=gettext("Choose which interface this rule applies to"); ?>.<br />
|
||||
<?=gettext("Hint: in most cases, you'll want to use WAN here"); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_type" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Type"); ?></td>
|
||||
<td>
|
||||
<select name="type" class="selectpicker" data-width="348px" id="nattype">
|
||||
<option value="binat" <?=$pconfig['type'] == 'binat' || empty($pconfig['type']) ? "selected=\"selected\"" : ""; ?>>
|
||||
<?=gettext("BINAT");?>
|
||||
</option>
|
||||
<option value="nat" <?=$pconfig['type'] == 'nat' ? "selected=\"selected\"" : ""; ?>>
|
||||
<?=gettext("NAT");?>
|
||||
</option>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_type">
|
||||
<?=gettext("Select BINAT (default) or NAT here, when nets are equally sized binat is usually the best option.".
|
||||
"Using NAT we can also map unequal sized networks.");?><br />
|
||||
<?=gettext("A BINAT rule specifies a bidirectional mapping between an external and internal network and can be used from both ends, nat only applies in one direction.");?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_external" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("External network"); ?></td>
|
||||
<td>
|
||||
<input name="external" type="text" value="<?=$pconfig['external'];?>" />
|
||||
<div class="hidden" data-for="help_for_external">
|
||||
<?=gettext("Enter the external subnet's starting address for the 1:1 mapping or network.");?><br />
|
||||
<?=gettext("The subnet mask from the internal address below will be applied to this IP address, when none is provided."); ?><br />
|
||||
<?=gettext("This is the address or network the traffic will translate to/from.");?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_src_invert" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Source") . " / ".gettext("Invert");?> </td>
|
||||
<td>
|
||||
<input name="srcnot" type="checkbox" id="srcnot" value="yes" <?= !empty($pconfig['srcnot']) ? "checked=\"checked\"" : "";?> />
|
||||
<div class="hidden" data-for="help_for_src_invert">
|
||||
<?=gettext("Use this option to invert the sense of the match."); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_src" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Source"); ?></td>
|
||||
<td>
|
||||
<table style="max-width: 348px">
|
||||
<tr>
|
||||
<td>
|
||||
<select name="src" id="src" class="selectpicker" data-live-search="true" data-size="5" data-width="348px" data-hide-disabled="true">
|
||||
<option data-other=true value="<?=$pconfig['src'];?>" <?=!is_specialnet($pconfig['src']) ? "selected=\"selected\"" : "";?>><?=gettext("Single host or Network"); ?></option>
|
||||
<optgroup label="<?=gettext("Aliases");?>" data-type="nat">
|
||||
<?php foreach (legacy_list_aliases("network") as $alias):
|
||||
?>
|
||||
<option value="<?=$alias['name'];?>" <?=$alias['name'] == $pconfig['src'] ? "selected=\"selected\"" : "";?>><?=htmlspecialchars($alias['name']);?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
<optgroup label="<?=gettext("Networks");?>" data-type="nat">
|
||||
<?php foreach (get_specialnets(true) as $ifent => $ifdesc):
|
||||
?>
|
||||
<option value="<?=$ifent;?>" <?= $pconfig['src'] == $ifent ? "selected=\"selected\"" : ""; ?>><?=$ifdesc;?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- updates to "other" option in src -->
|
||||
<table style="max-width: 348px">
|
||||
<tr>
|
||||
<td style="width:285px">
|
||||
<input type="text" for="src" value="<?=$pconfig['src'];?>" aria-label="<?=gettext("Source address");?>"/>
|
||||
</td>
|
||||
<td>
|
||||
<select name="srcmask" class="selectpicker" data-size="5" id="srcmask" data-width="70px" for="src" >
|
||||
<?php for ($i = 32; $i > 0; $i--): ?>
|
||||
<option value="<?=$i;?>" <?= $i == $pconfig['srcmask'] ? "selected=\"selected\"" : ""; ?>><?=$i;?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="hidden" data-for="help_for_src">
|
||||
<?=gettext("Enter the internal subnet for the 1:1 mapping. The subnet size specified for the source will be applied to the external subnet, when none is provided."); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <a id="help_for_dst_invert" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Destination") . " / ".gettext("Invert");?> </td>
|
||||
<td>
|
||||
<input name="dstnot" type="checkbox" id="srcnot" value="yes" <?= !empty($pconfig['dstnot']) ? "checked=\"checked\"" : "";?> />
|
||||
<div class="hidden" data-for="help_for_dst_invert">
|
||||
<?=gettext("Use this option to invert the sense of the match."); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_dst" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Destination"); ?></td>
|
||||
<td>
|
||||
<table style="max-width:348px">
|
||||
<tr>
|
||||
<td>
|
||||
<select name="dst" id="dst" class="selectpicker" data-live-search="true" data-size="5" data-width="348px">
|
||||
<option data-other=true value="<?=$pconfig['dst'];?>" <?=!is_specialnet($pconfig['dst']) ? "selected=\"selected\"" : "";?>><?=gettext("Single host or Network"); ?></option>
|
||||
<optgroup label="<?=gettext("Aliases");?>">
|
||||
<?php foreach (legacy_list_aliases("network") as $alias):
|
||||
?>
|
||||
<option value="<?=$alias['name'];?>" <?=$alias['name'] == $pconfig['dst'] ? "selected=\"selected\"" : "";?>><?=htmlspecialchars($alias['name']);?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
<optgroup label="<?=gettext("Networks");?>">
|
||||
<?php foreach (get_specialnets(true) as $ifent => $ifdesc):
|
||||
?>
|
||||
<option value="<?=$ifent;?>" <?= $pconfig['dst'] == $ifent ? "selected=\"selected\"" : ""; ?>><?=$ifdesc;?></option>
|
||||
<?php endforeach; ?>
|
||||
</optgroup>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- updates to "other" option in dst -->
|
||||
<table style="max-width:348px">
|
||||
<tr>
|
||||
<td style="width:285px">
|
||||
<input type="text" for="dst" value="<?= !is_specialnet($pconfig['dst']) ? $pconfig['dst'] : "";?>" aria-label="<?=gettext("Destination address");?>"/>
|
||||
</td>
|
||||
<td>
|
||||
<select name="dstmask" class="selectpicker" data-size="5" id="dstmask" data-width="70px" for="dst" >
|
||||
<?php for ($i = 32; $i > 0; $i--): ?>
|
||||
<option value="<?=$i;?>" <?= $i == $pconfig['dstmask'] ? "selected=\"selected\"" : ""; ?>><?=$i;?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="hidden" data-for="help_for_dst">
|
||||
<?=gettext("The 1:1 mapping will only be used for connections to or from the specified destination."); ?><br />
|
||||
<?=gettext("Hint: this is usually 'any'."); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_category" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Category"); ?></td>
|
||||
<td>
|
||||
<select name="category[]" id="category" multiple="multiple" class="tokenize" data-allownew="true" data-width="348px" data-live-search="true">
|
||||
<?php
|
||||
foreach ((new OPNsense\Firewall\Category())->iterateCategories() as $category):
|
||||
$catname = htmlspecialchars($category['name'], ENT_QUOTES | ENT_HTML401);?>
|
||||
<option value="<?=$catname;?>" <?=!empty($pconfig['category']) && in_array($catname, $pconfig['category']) ? 'selected="selected"' : '';?> ><?=$catname;?></option>
|
||||
<?php
|
||||
endforeach;?>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_category">
|
||||
<?=gettext("You may enter or select a category here to group firewall rules (not parsed)."); ?>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_descr" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description"); ?></td>
|
||||
<td>
|
||||
<input name="descr" type="text" id="descr" size="40" value="<?= $pconfig['descr'] ?>" />
|
||||
<div class="hidden" data-for="help_for_descr">
|
||||
<?=gettext("You may enter a description here for your reference (not parsed)."); ?>
|
||||
</div>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("NAT reflection"); ?></td>
|
||||
<td>
|
||||
<select name="natreflection" class="selectpicker">
|
||||
<option value="default" <?=$pconfig['natreflection'] != "enable" && $pconfig['natreflection'] != "disable" ? "selected=\"selected\"" : ""; ?>><?=gettext("Use system default"); ?></option>
|
||||
<option value="enable" <?=$pconfig['natreflection'] == "enable" ? "selected=\"selected\"" : ""; ?>><?=gettext("Enable"); ?></option>
|
||||
<option value="disable" <?=$pconfig['natreflection'] == "disable" ? "selected=\"selected\"" : ""; ?>><?=gettext("Disable"); ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input name="Submit" type="submit" class="btn btn-primary" value="<?=html_safe(gettext('Save')); ?>" />
|
||||
<input type="button" class="btn btn-default" value="<?=html_safe(gettext('Cancel'));?>" onclick="window.location.href='/firewall_nat_1to1.php'" />
|
||||
<?php if (isset($id)): ?>
|
||||
<input name="id" type="hidden" value="<?=$id;?>" />
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php include("foot.inc"); ?>
|
||||
Loading…
x
Reference in New Issue
Block a user