From 8fae468c0f5bab98c94ef480df509ca6939c6865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20R=C3=B6nnvall?= Date: Tue, 15 Mar 2016 16:10:01 +0100 Subject: [PATCH 1/3] Add field to specify source-hash key The source-hash pool option uses a hash of the source address to determine the translation address. This hashing algorithm is also fed a key, which unless specified defaults to a random value. This random value is then generated each time pf is reloaded. This commit adds the ability to specify the key in order to provide consistent hashing, even when pf is reloaded. --- src/usr/local/www/firewall_nat_out_edit.php | 34 +++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/usr/local/www/firewall_nat_out_edit.php b/src/usr/local/www/firewall_nat_out_edit.php index db2b6846a2..503c496d75 100644 --- a/src/usr/local/www/firewall_nat_out_edit.php +++ b/src/usr/local/www/firewall_nat_out_edit.php @@ -126,7 +126,13 @@ if (isset($id) && $a_out[$id]) { $pconfig['target'] = $a_out[$id]['target']; $pconfig['targetip'] = $a_out[$id]['targetip']; $pconfig['targetip_subnet'] = $a_out[$id]['targetip_subnet']; - $pconfig['poolopts'] = $a_out[$id]['poolopts']; + if (substr($a_out[$id]['poolopts'],0,11) == 'source-hash'){ + list($opts, $key) = split(" ",$a_out[$id]['poolopts']); + $pconfig['source-hash-key']=$key; + $pconfig['poolopts']=$opts; + }else{ + $pconfig['poolopts']=$a_out[$id]['poolopts']; + } $pconfig['interface'] = $a_out[$id]['interface']; if (!$pconfig['interface']) { @@ -267,6 +273,9 @@ if ($_POST) { $input_errors[] = gettext("Only Round Robin pool options may be chosen when selecting an alias."); } } + if ($_POST['source-hash-key']){ + $source_hash_key = $_POST['source-hash-key']; + } } /* if user has selected any as source, set it here */ @@ -308,7 +317,11 @@ if ($_POST) { $natent['targetip'] = (!isset($_POST['nonat'])) ? $_POST['targetip'] : ""; $natent['targetip_subnet'] = (!isset($_POST['nonat'])) ? $_POST['targetip_subnet'] : ""; $natent['interface'] = $_POST['interface']; - $natent['poolopts'] = $poolopts; + if($poolopts == 'source-hash' && isset($source_hash_key)){ + $natent['poolopts'] = $poolopts." ".$source_hash_key; + }else{ + $natent['poolopts'] = $poolopts; + } /* static-port */ if (isset($_POST['staticnatport']) && $protocol_uses_ports && !isset($_POST['nonat'])) { @@ -601,6 +614,13 @@ $section->addInput(new Form_Select( '
  • ' . 'Sticky Address: The Sticky Address option can be used with the Random and Round Robin pool types to ensure that a particular source address is always mapped to the same translation address.' . '
  • ' . ''); +$section->addInput(new Form_Input( + 'source-hash-key', + 'Source Hash Key', + 'text', + $pconfig['source-hash-key'] +))->setHelp('The key that is fed to the hashing algorithm in hex format or as a string, defaults to a randomly generated value.')->setWidth(10)->addClass('othersubnet'); + $group = new Form_Group('Port'); $group->addClass('natportgrp'); @@ -749,10 +769,16 @@ events.push(function() { } else if ($('#target option:selected').text().trim().substring(0,5) == "Other") { hideInput('poolopts', false); hideGroupClass('othersubnet', false); + if ($('#poolopts option:selected').text().trim().substring(0,6) == "Source") { + hideInput('source-hash-key', false); + }else { + hideInput('source-hash-key', true); + } } else { $('#poolopts').prop('selectedIndex',0); hideInput('poolopts', true); hideGroupClass('othersubnet', true); + hideInput('source-hash-key', true); $('#targetip').val(''); $('#targetip_subnet').val('0'); } @@ -783,6 +809,10 @@ events.push(function() { poolopts_change(); }); + $('#poolopts').on('change', function() { + poolopts_change(); + }); + // Set initial states staticportchange(); sourcesel_change(); From b2e511600bc804e77251b332e0d7d256828b794e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20R=C3=B6nnvall?= Date: Mon, 21 Mar 2016 11:25:06 +0100 Subject: [PATCH 2/3] Improve handling of source-hash key - Store the source-hash key in its own config field. - Validate the provided source-hash key. Check that hex string input is of the form "0x" followed by 32 hexadecimal digits. Any other string not starting with "0x" is hashed using md5 and stored as "0x" followed by the md5 hash. - Correct style issues making sure to follow pfSense Developer Style Guide. - Addition of the stored source-hash key config field in filter.inc, append it to the poolopts variable --- src/etc/inc/filter.inc | 5 +++ src/usr/local/www/firewall_nat_out_edit.php | 42 +++++++++++---------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/etc/inc/filter.inc b/src/etc/inc/filter.inc index f006a1f77d..c0988961d2 100644 --- a/src/etc/inc/filter.inc +++ b/src/etc/inc/filter.inc @@ -2066,6 +2066,11 @@ function filter_nat_rules_generate() { $obtarget = ($obent['target'] == "other-subnet") ? $obent['targetip'] . '/' . $obent['targetip_subnet']: $obent['target']; $poolopts = (is_subnet($obtarget) || is_alias($obtarget)) ? $obent['poolopts'] : ""; + /* pool option source-hash allows specification of an optional source-hash key */ + if ($poolopts == "source-hash" && isset($obent['source_hash_key'])) { + $poolopts = "source-hash ".$obent['source_hash_key']; + } + $natrules .= filter_nat_rules_generate_if($obent['interface'], $src, $obent['sourceport'], diff --git a/src/usr/local/www/firewall_nat_out_edit.php b/src/usr/local/www/firewall_nat_out_edit.php index 503c496d75..3839e1a109 100644 --- a/src/usr/local/www/firewall_nat_out_edit.php +++ b/src/usr/local/www/firewall_nat_out_edit.php @@ -126,13 +126,8 @@ if (isset($id) && $a_out[$id]) { $pconfig['target'] = $a_out[$id]['target']; $pconfig['targetip'] = $a_out[$id]['targetip']; $pconfig['targetip_subnet'] = $a_out[$id]['targetip_subnet']; - if (substr($a_out[$id]['poolopts'],0,11) == 'source-hash'){ - list($opts, $key) = split(" ",$a_out[$id]['poolopts']); - $pconfig['source-hash-key']=$key; - $pconfig['poolopts']=$opts; - }else{ - $pconfig['poolopts']=$a_out[$id]['poolopts']; - } + $pconfig['poolopts'] = $a_out[$id]['poolopts']; + $pconfig['source_hash_key'] = $a_out[$id]['source_hash_key']; $pconfig['interface'] = $a_out[$id]['interface']; if (!$pconfig['interface']) { @@ -263,6 +258,7 @@ if ($_POST) { /* Verify Pool Options */ $poolopts = ""; + $source_hash_key = ""; if ($_POST['poolopts']) { if (is_subnet($_POST['target']) || ($_POST['target'] == "other-subnet")) { $poolopts = $_POST['poolopts']; @@ -273,8 +269,17 @@ if ($_POST) { $input_errors[] = gettext("Only Round Robin pool options may be chosen when selecting an alias."); } } - if ($_POST['source-hash-key']){ - $source_hash_key = $_POST['source-hash-key']; + /* If specified, verify valid source-hash key or generate a valid key using md5 */ + if ($_POST['source_hash_key']) { + if (substr($_POST['source_hash_key'],0,2) == "0x") { + if (ctype_xdigit(substr($_POST['source_hash_key'],2)) && strlen($_POST['source_hash_key']) == 34) { + $source_hash_key = $_POST['source_hash_key']; + } else { + $input_errors[] = gettext("Incorrect format for source-hash key, \"0x\" must be followed by exactly 32 hexadecimal characters."); + } + } else { + $source_hash_key = "0x".md5($_POST['source_hash_key']); + } } } @@ -317,11 +322,8 @@ if ($_POST) { $natent['targetip'] = (!isset($_POST['nonat'])) ? $_POST['targetip'] : ""; $natent['targetip_subnet'] = (!isset($_POST['nonat'])) ? $_POST['targetip_subnet'] : ""; $natent['interface'] = $_POST['interface']; - if($poolopts == 'source-hash' && isset($source_hash_key)){ - $natent['poolopts'] = $poolopts." ".$source_hash_key; - }else{ - $natent['poolopts'] = $poolopts; - } + $natent['poolopts'] = $poolopts; + $natent['source_hash_key'] = $source_hash_key; /* static-port */ if (isset($_POST['staticnatport']) && $protocol_uses_ports && !isset($_POST['nonat'])) { @@ -615,11 +617,11 @@ $section->addInput(new Form_Select( ''); $section->addInput(new Form_Input( - 'source-hash-key', + 'source_hash_key', 'Source Hash Key', 'text', - $pconfig['source-hash-key'] -))->setHelp('The key that is fed to the hashing algorithm in hex format or as a string, defaults to a randomly generated value.')->setWidth(10)->addClass('othersubnet'); + $pconfig['source_hash_key'] +))->setHelp('The key that is fed to the hashing algorithm in hex format, preceeded by "0x", or any string. A non-hex string is hashed using md5 to a hexadecimal key. Defaults to a randomly generated value.')->setWidth(10)->addClass('othersubnet'); $group = new Form_Group('Port'); $group->addClass('natportgrp'); @@ -770,15 +772,15 @@ events.push(function() { hideInput('poolopts', false); hideGroupClass('othersubnet', false); if ($('#poolopts option:selected').text().trim().substring(0,6) == "Source") { - hideInput('source-hash-key', false); + hideInput('source_hash_key', false); }else { - hideInput('source-hash-key', true); + hideInput('source_hash_key', true); } } else { $('#poolopts').prop('selectedIndex',0); hideInput('poolopts', true); hideGroupClass('othersubnet', true); - hideInput('source-hash-key', true); + hideInput('source_hash_key', true); $('#targetip').val(''); $('#targetip_subnet').val('0'); } From 6a9d1bfc5c90011af10a1704231340a42fa9f51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20R=C3=B6nnvall?= Date: Thu, 15 Sep 2016 10:28:34 +0200 Subject: [PATCH 3/3] Use !empty() instead of isset() --- src/etc/inc/filter.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etc/inc/filter.inc b/src/etc/inc/filter.inc index c0988961d2..73ffc2edb3 100644 --- a/src/etc/inc/filter.inc +++ b/src/etc/inc/filter.inc @@ -2067,7 +2067,7 @@ function filter_nat_rules_generate() { $poolopts = (is_subnet($obtarget) || is_alias($obtarget)) ? $obent['poolopts'] : ""; /* pool option source-hash allows specification of an optional source-hash key */ - if ($poolopts == "source-hash" && isset($obent['source_hash_key'])) { + if ($poolopts == "source-hash" && !empty($obent['source_hash_key'])) { $poolopts = "source-hash ".$obent['source_hash_key']; }