diff --git a/src/www/firewall_rules_edit.php b/src/www/firewall_rules_edit.php
index 430304f9d..17b9b3c09 100644
--- a/src/www/firewall_rules_edit.php
+++ b/src/www/firewall_rules_edit.php
@@ -86,6 +86,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
} elseif (isset($_GET['id']) && isset($a_filter[$_GET['id']])) {
$id = $_GET['id'];
$configId = $id;
+ } elseif (isset($_GET['get_address_options'])) {
+ /* XXX: no beauty contest here, we need the same valid options as MVC, just dump them... */
+ echo json_encode((new OPNsense\Firewall\Api\FilterController())->listNetworkSelectOptionsAction());
+ exit(0);
}
// define form fields
@@ -149,12 +153,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$pconfig['category'] = !empty($pconfig['category']) ? explode(",", $pconfig['category']) : [];
// process fields with some kind of logic
- address_to_pconfig($a_filter[$configId]['source'], $pconfig['src'],
- $pconfig['srcmask'], $pconfig['srcnot'],
- $pconfig['srcbeginport'], $pconfig['srcendport']);
- address_to_pconfig($a_filter[$configId]['destination'], $pconfig['dst'],
- $pconfig['dstmask'], $pconfig['dstnot'],
- $pconfig['dstbeginport'], $pconfig['dstendport']);
+ address_to_pconfig(
+ $a_filter[$configId]['source'],
+ $pconfig['src'],
+ $ignore, /* XXX: ignored */
+ $pconfig['srcnot'],
+ $pconfig['srcbeginport'],
+ $pconfig['srcendport'],
+ true
+ );
+
+ address_to_pconfig(
+ $a_filter[$configId]['destination'],
+ $pconfig['dst'],
+ $ignore, /* XXX: ignored */
+ $pconfig['dstnot'],
+ $pconfig['dstbeginport'],
+ $pconfig['dstendport'],
+ true
+ );
+
if (isset($id) && isset($a_filter[$configId]['associated-rule-id'])) {
// do not link on rule copy.
$pconfig['associated-rule-id'] = $a_filter[$configId]['associated-rule-id'];
@@ -294,18 +312,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$input_errors[] = gettext('When selecting aliases for destination ports, both from and to fields must be the same');
}
}
- if (!is_specialnet($pconfig['src']) && !is_ipaddroralias($pconfig['src'])) {
+ if (strpos($pconfig['src'], ',') > 0) {
+ foreach (explode(',', $pconfig['src']) as $tmp) {
+ if (!is_specialnet($tmp) && !is_alias($tmp)) {
+ $input_errors[] = sprintf(gettext("%s is not a valid source alias."), $tmp);
+ }
+ }
+ } elseif (!is_specialnet($pconfig['src']) && !is_ipaddroralias($pconfig['src']) && !is_subnet($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.");
- }
- if (!is_specialnet($pconfig['dst']) && !is_ipaddroralias($pconfig['dst'])) {
+ if (strpos($pconfig['dst'], ',') > 0) {
+ foreach (explode(',', $pconfig['dst']) as $tmp) {
+ if (!is_specialnet($tmp) && !is_alias($tmp)) {
+ $input_errors[] = sprintf(gettext("%s is not a valid destination alias."), $tmp);
+ }
+ }
+ } elseif (!is_specialnet($pconfig['dst']) && !is_ipaddroralias($pconfig['dst']) && !is_subnet($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 (is_ipaddr($pconfig['src']) && is_ipaddr($pconfig['dst'])) {
if ((is_ipaddrv4($pconfig['src']) && is_ipaddrv6($pconfig['dst'])) || (is_ipaddrv6($pconfig['src']) && is_ipaddrv4($pconfig['dst']))) {
@@ -314,21 +338,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
}
foreach (array('src', 'dst') as $fam) {
if (is_ipaddr($pconfig[$fam])) {
- if (is_ipaddrv6($pconfig[$fam]) && $pconfig['ipprotocol'] == "inet") {
+ if ((is_ipaddrv6($pconfig[$fam]) || is_subnetv6($pconfig[$fam])) && $pconfig['ipprotocol'] == "inet") {
$input_errors[] = gettext("You can not use IPv6 addresses in IPv4 rules.");
- } elseif (is_ipaddrv4($pconfig[$fam]) && $pconfig['ipprotocol'] == "inet6") {
+ } elseif ((is_ipaddrv4($pconfig[$fam]) || is_subnetv4($pconfig[$fam])) && $pconfig['ipprotocol'] == "inet6") {
$input_errors[] = gettext("You can not use IPv4 addresses in IPv6 rules.");
}
}
}
- if (is_ipaddrv4($pconfig['src']) && $pconfig['srcmask'] > 32) {
- $input_errors[] = gettext("Invalid subnet mask on IPv4 source");
- }
- if (is_ipaddrv4($pconfig['dst']) && $pconfig['dstmask'] > 32) {
- $input_errors[] = gettext("Invalid subnet mask on IPv4 destination");
- }
-
if ((is_ipaddr($pconfig['src']) || is_ipaddr($pconfig['dst'])) && ($pconfig['ipprotocol'] == "inet46")) {
$input_errors[] = gettext('You can not use an IPv4 or IPv6 address in combined IPv4 + IPv6 rules.');
}
@@ -395,7 +412,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (is_numeric($pconfig['adaptivestart']) || is_numeric($pconfig['adaptiveend'])) {
$input_errors[] = gettext("You cannot specify the adaptive timeouts (advanced option) if statetype is none.");
}
-
}
if (!empty($pconfig['max']) && !is_posnumericint($pconfig['max']))
@@ -586,10 +602,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
}
pconfig_to_address($filterent['source'], $pconfig['src'],
- $pconfig['srcmask'] ?? '', !empty($pconfig['srcnot']),
+ '', !empty($pconfig['srcnot']),
$pconfig['srcbeginport'], $pconfig['srcendport']);
pconfig_to_address($filterent['destination'], $pconfig['dst'],
- $pconfig['dstmask'] ?? '', !empty($pconfig['dstnot']),
+ '', !empty($pconfig['dstnot']),
$pconfig['dstbeginport'], $pconfig['dstendport']);
$filterent['updated'] = make_config_revision_entry();
@@ -652,10 +668,54 @@ include("head.inc");
$(".advanced_opt_src").toggleClass("hidden visible");
});
+ $.ajax("/firewall_rules_edit.php?get_address_options",{
+ type: 'get',
+ cache: false,
+ dataType: "json",
+ success: function(data) {
+ $(".net_selector_multi").each(function(){
+ /* replaceInputWithSelector() replaces our input with a clean one, copy relevant attributes after construct */
+ $(this).replaceInputWithSelector(data, true);
+ let new_input = $("#" + $(this).attr('id'));
+ new_input.attr('name', $(this).attr('id'));
+ if ($(this).is(':disabled')) {
+ $("select[for='" + $(this).attr('id') + "']").prop('disabled', true);
+ new_input.prop('disabled', true);
+ }
+ $("select[for='" + $(this).attr('id') + "']").on('shown.bs.select', function(){
+ $(this).data('previousValue', $(this).val());
+ }).change(function(){
+ let prev = Array.isArray($(this).data('previousValue')) ? $(this).data('previousValue') : [];
+ let is_single = $(this).val().includes('') || $(this).val().includes('any');
+ let was_single = prev.includes('') || prev.includes('any');
+ let refresh = false;
+ if (was_single && is_single && $(this).val().length > 1) {
+ $(this).val($(this).val().filter(value => !prev.includes(value)));
+ refresh = true;
+ } else if (is_single && $(this).val().length > 1) {
+ if ($(this).val().includes('any') && !prev.includes('any')) {
+ $(this).val('any');
+ } else{
+ $(this).val('');
+ }
+ refresh = true;
+ }
+ if (refresh) {
+ $(this).selectpicker('refresh');
+ $(this).trigger('change');
+ }
+ $(this).data('previousValue', $(this).val());
+ });
+ new_input.val($(this).val()).change();
+ });
+ }
+ });
+
+
// 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(){
+ $('.portselect').each(function(){
var refObj = $("#"+$(this).attr("for"));
if (refObj.is("select")) {
// connect on change event to select box (show/hide)
@@ -689,7 +749,7 @@ include("head.inc");
if ($(this).attr("name") == undefined) {
$(this).change(function(){
var otherOpt = $('#'+$(this).attr('for')+' > option[data-other="true"]') ;
- otherOpt.attr("value",$(this).val());
+ otherOpt.attr("value", $(this).val());
});
}
}
@@ -1064,51 +1124,8 @@ include("head.inc");
| =gettext("Source"); ?> |
-
-
- |
-
- |
-
-
- |
-
- |
-
-
- |
+ id="src" name="src" class="net_selector_multi" type="text" value="=$pconfig['src'];?>" />
+
| =gettext("Source"); ?> |
@@ -1168,10 +1185,10 @@ include("head.inc");
|
- type="text" value="=$pconfig['srcbeginport'];?>" for="srcbeginport">
+ type="text" value="=$pconfig['srcbeginport'];?>" class="portselect" for="srcbeginport">
|
- type="text" value="=$pconfig['srcendport'];?>" for="srcendport">
+ type="text" value="=$pconfig['srcendport'];?>" class="portselect" for="srcendport">
|
@@ -1192,48 +1209,7 @@ include("head.inc");
| =gettext("Destination"); ?> |
-
-
- |
-
- |
-
-
- |
-
- |
-
-
+ id="dst" name="dst" type="text" value="=$pconfig['dst'];?>" class="net_selector_multi" />
|
@@ -1285,10 +1261,10 @@ include("head.inc");
|
- type="text" value="=$pconfig['dstbeginport'];?>" for="dstbeginport">
+ class="portselect" type="text" value="=$pconfig['dstbeginport'];?>" for="dstbeginport">
|
- type="text" value="=$pconfig['dstendport'];?>" for="dstendport">
+ class="portselect" type="text" value="=$pconfig['dstendport'];?>" for="dstendport">
|
diff --git a/src/www/guiconfig.inc b/src/www/guiconfig.inc
index 96a36ec4e..b70fc0adf 100644
--- a/src/www/guiconfig.inc
+++ b/src/www/guiconfig.inc
@@ -385,7 +385,7 @@ function gentitle($breadcrumbs, $navlevelsep = ': ')
return join($navlevelsep, $output) . "$gentitle_suffix";
}
-function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport)
+function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendport, $merge_mask=false)
{
if (isset($adr['any'])) {
$padr = "any";
@@ -402,6 +402,9 @@ function address_to_pconfig($adr, &$padr, &$pmask, &$pnot, &$pbeginport, &$pendp
$pmask = 32;
}
}
+ if ($merge_mask && is_ipaddr($padr)) {
+ $padr = $padr . '/' . $pmask;
+ }
}
if (isset($adr['not'])) {